2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #define __STDC_LIMIT_MACROS 1
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
31 #include "audio_region_view.h"
32 #include "midi_region_view.h"
33 #include "ardour_ui.h"
34 #include "gui_thread.h"
35 #include "control_point.h"
37 #include "region_gain_line.h"
38 #include "editor_drag.h"
39 #include "audio_time_axis.h"
40 #include "midi_time_axis.h"
41 #include "canvas-note.h"
42 #include "selection.h"
43 #include "midi_selection.h"
44 #include "automation_time_axis.h"
47 using namespace ARDOUR;
50 using namespace Editing;
51 using namespace ArdourCanvas;
53 using Gtkmm2ext::Keyboard;
55 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
57 DragManager::DragManager (Editor* e)
60 , _current_pointer_frame (0)
65 DragManager::~DragManager ()
70 /** Call abort for each active drag */
76 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
87 DragManager::add (Drag* d)
89 d->set_manager (this);
94 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
96 assert (_drags.empty ());
97 d->set_manager (this);
103 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
105 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
107 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 (*i)->start_grab (e, c);
112 /** Call end_grab for each active drag.
113 * @return true if any drag reported movement having occurred.
116 DragManager::end_grab (GdkEvent* e)
121 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
122 bool const t = (*i)->end_grab (e);
137 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
141 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->motion_handler (e, from_autoscroll);
155 DragManager::have_item (ArdourCanvas::Item* i) const
157 list<Drag*>::const_iterator j = _drags.begin ();
158 while (j != _drags.end() && (*j)->item () != i) {
162 return j != _drags.end ();
165 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
168 , _pointer_frame_offset (0)
169 , _move_threshold_passed (false)
171 , _last_pointer_frame (0)
177 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
183 cursor = _editor->which_grabber_cursor ();
186 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
190 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
193 cursor = _editor->which_grabber_cursor ();
196 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
198 if (Keyboard::is_button2_event (&event->button)) {
199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
200 _y_constrained = true;
201 _x_constrained = false;
203 _y_constrained = false;
204 _x_constrained = true;
207 _x_constrained = false;
208 _y_constrained = false;
211 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
212 _grab_frame = adjusted_frame (_grab_frame, event);
213 _last_pointer_frame = _grab_frame;
214 _last_pointer_x = _grab_x;
215 _last_pointer_y = _grab_y;
217 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
221 if (_editor->session() && _editor->session()->transport_rolling()) {
224 _was_rolling = false;
227 switch (_editor->snap_type()) {
228 case SnapToRegionStart:
229 case SnapToRegionEnd:
230 case SnapToRegionSync:
231 case SnapToRegionBoundary:
232 _editor->build_region_boundary_cache ();
239 /** Call to end a drag `successfully'. Ungrabs item and calls
240 * subclass' finished() method.
242 * @param event GDK event, or 0.
243 * @return true if some movement occurred, otherwise false.
246 Drag::end_grab (GdkEvent* event)
248 _editor->stop_canvas_autoscroll ();
250 _item->ungrab (event ? event->button.time : 0);
252 finished (event, _move_threshold_passed);
254 _editor->hide_verbose_canvas_cursor();
256 return _move_threshold_passed;
260 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
264 if (f > _pointer_frame_offset) {
265 pos = f - _pointer_frame_offset;
269 _editor->snap_to_with_modifier (pos, event);
276 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
278 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
282 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
284 /* check to see if we have moved in any way that matters since the last motion event */
285 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
286 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
290 pair<nframes64_t, int> const threshold = move_threshold ();
292 bool const old_move_threshold_passed = _move_threshold_passed;
294 if (!from_autoscroll && !_move_threshold_passed) {
296 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
297 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
299 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
302 if (active (_editor->mouse_mode) && _move_threshold_passed) {
304 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
305 if (!from_autoscroll) {
306 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
309 motion (event, _move_threshold_passed != old_move_threshold_passed);
311 _last_pointer_x = _drags->current_pointer_x ();
312 _last_pointer_y = _drags->current_pointer_y ();
313 _last_pointer_frame = adjusted_current_frame (event);
321 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
331 _editor->stop_canvas_autoscroll ();
332 _editor->hide_verbose_canvas_cursor ();
335 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
339 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
340 _views.push_back (DraggingView (*i));
343 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
347 RegionDrag::region_going_away (RegionView* v)
349 list<DraggingView>::iterator i = _views.begin ();
350 while (i != _views.end() && i->view != v) {
354 if (i != _views.end()) {
359 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
360 : RegionDrag (e, i, p, v),
371 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
373 Drag::start_grab (event);
375 _editor->show_verbose_time_cursor (_last_frame_position, 10);
378 RegionMotionDrag::TimeAxisViewSummary
379 RegionMotionDrag::get_time_axis_view_summary ()
381 int32_t children = 0;
382 TimeAxisViewSummary sum;
384 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
386 /* get a bitmask representing the visible tracks */
388 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
389 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
390 TimeAxisView::Children children_list;
392 /* zeroes are audio/MIDI tracks. ones are other types. */
394 if (!rtv->hidden()) {
396 if (!rtv->is_track()) {
397 /* not an audio nor MIDI track */
398 sum.tracks = sum.tracks |= (0x01 << rtv->order());
401 sum.height_list[rtv->order()] = (*i)->current_height();
404 if ((children_list = rtv->get_child_list()).size() > 0) {
405 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
406 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
407 sum.height_list[rtv->order() + children] = (*j)->current_height();
418 RegionMotionDrag::compute_y_delta (
419 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
420 int32_t last_pointer_layer, int32_t current_pointer_layer,
421 TimeAxisViewSummary const & tavs,
422 int32_t* pointer_order_span, int32_t* pointer_layer_span,
423 int32_t* canvas_pointer_order_span
427 *pointer_order_span = 0;
428 *pointer_layer_span = 0;
432 bool clamp_y_axis = false;
434 /* the change in track order between this callback and the last */
435 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
436 /* the change in layer between this callback and the last;
437 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
438 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
440 if (*pointer_order_span != 0) {
442 /* find the actual pointer span, in terms of the number of visible tracks;
443 to do this, we reduce |pointer_order_span| by the number of hidden tracks
446 *canvas_pointer_order_span = *pointer_order_span;
447 if (last_pointer_view->order() >= current_pointer_view->order()) {
448 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
449 if (tavs.height_list[y] == 0) {
450 *canvas_pointer_order_span--;
454 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
455 if (tavs.height_list[y] == 0) {
456 *canvas_pointer_order_span++;
461 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
463 RegionView* rv = i->view;
465 if (rv->region()->locked()) {
469 double ix1, ix2, iy1, iy2;
470 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
471 rv->get_canvas_frame()->i2w (ix1, iy1);
472 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
474 /* get the new trackview for this particular region */
475 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
477 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
479 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
480 as surely this is a per-region thing... */
482 clamp_y_axis = y_movement_disallowed (
483 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
491 } else if (_dest_trackview == current_pointer_view) {
493 if (current_pointer_layer == last_pointer_layer) {
494 /* No movement; clamp */
500 _dest_trackview = current_pointer_view;
501 _dest_layer = current_pointer_layer;
509 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
511 /* compute the amount of pointer motion in frames, and where
512 the region would be if we moved it by that much.
514 *pending_region_position = adjusted_current_frame (event);
516 nframes64_t sync_frame;
517 nframes64_t sync_offset;
520 sync_offset = _primary->region()->sync_offset (sync_dir);
522 /* we don't handle a sync point that lies before zero.
524 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
526 sync_frame = *pending_region_position + (sync_dir*sync_offset);
528 _editor->snap_to_with_modifier (sync_frame, event);
530 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
533 *pending_region_position = _last_frame_position;
536 if (*pending_region_position > max_frames - _primary->region()->length()) {
537 *pending_region_position = _last_frame_position;
542 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
544 /* x movement since last time */
545 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
547 /* total x movement */
548 framecnt_t total_dx = *pending_region_position;
549 if (regions_came_from_canvas()) {
550 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
553 /* check that no regions have gone off the start of the session */
554 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
555 if ((i->view->region()->position() + total_dx) < 0) {
557 *pending_region_position = _last_frame_position;
562 _last_frame_position = *pending_region_position;
569 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
573 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
575 vector<int32_t>::iterator j;
577 /* *pointer* variables reflect things about the pointer; as we may be moving
578 multiple regions, much detail must be computed per-region */
580 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
581 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
582 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
583 is always 0 regardless of what the region's "real" layer is */
584 RouteTimeAxisView* current_pointer_view;
585 layer_t current_pointer_layer;
586 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
590 /* TimeAxisView that we were pointing at last time we entered this method */
591 TimeAxisView const * const last_pointer_view = _dest_trackview;
592 /* the order of the track that we were pointing at last time we entered this method */
593 int32_t const last_pointer_order = last_pointer_view->order ();
594 /* the layer that we were pointing at last time we entered this method */
595 layer_t const last_pointer_layer = _dest_layer;
597 int32_t pointer_order_span;
598 int32_t pointer_layer_span;
599 int32_t canvas_pointer_order_span;
601 bool const clamp_y_axis = compute_y_delta (
602 last_pointer_view, current_pointer_view,
603 last_pointer_layer, current_pointer_layer, tavs,
604 &pointer_order_span, &pointer_layer_span,
605 &canvas_pointer_order_span
608 nframes64_t pending_region_position;
609 double const x_delta = compute_x_delta (event, &pending_region_position);
611 /*************************************************************
613 ************************************************************/
615 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
616 /* haven't reached next snap point, and we're not switching
617 trackviews nor layers. nothing to do.
622 /*************************************************************
624 ************************************************************/
626 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
628 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
630 RegionView* rv = i->view;
632 if (rv->region()->locked()) {
636 /* here we are calculating the y distance from the
637 top of the first track view to the top of the region
638 area of the track view that we're working on */
640 /* this x value is just a dummy value so that we have something
645 /* distance from the top of this track view to the region area
646 of our track view is always 1 */
650 /* convert to world coordinates, ie distance from the top of
653 rv->get_canvas_frame()->i2w (ix1, iy1);
655 /* compensate for the ruler section and the vertical scrollbar position */
656 iy1 += _editor->get_trackview_group_vertical_offset ();
660 // hide any dependent views
662 rv->get_time_axis_view().hide_dependent_views (*rv);
665 reparent to a non scrolling group so that we can keep the
666 region selection above all time axis views.
667 reparenting means we have to move the rv as the two
668 parent groups have different coordinates.
671 rv->get_canvas_group()->property_y() = iy1 - 1;
672 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
674 rv->fake_set_opaque (true);
677 /* current view for this particular region */
678 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
679 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
681 if (pointer_order_span != 0 && !clamp_y_axis) {
683 /* INTER-TRACK MOVEMENT */
685 /* move through the height list to the track that the region is currently on */
686 vector<int32_t>::iterator j = tavs.height_list.begin ();
688 while (j != tavs.height_list.end () && x != rtv->order ()) {
694 int32_t temp_pointer_order_span = canvas_pointer_order_span;
696 if (j != tavs.height_list.end ()) {
698 /* Account for layers in the original and
699 destination tracks. If we're moving around in layers we assume
700 that only one track is involved, so it's ok to use *pointer*
703 StreamView* lv = last_pointer_view->view ();
706 /* move to the top of the last trackview */
707 if (lv->layer_display () == Stacked) {
708 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
711 StreamView* cv = current_pointer_view->view ();
714 /* move to the right layer on the current trackview */
715 if (cv->layer_display () == Stacked) {
716 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
719 /* And for being on a non-topmost layer on the new
722 while (temp_pointer_order_span > 0) {
723 /* we're moving up canvas-wise,
724 so we need to find the next track height
726 if (j != tavs.height_list.begin()) {
730 if (x != last_pointer_order) {
732 ++temp_pointer_order_span;
737 temp_pointer_order_span--;
740 while (temp_pointer_order_span < 0) {
744 if (x != last_pointer_order) {
746 --temp_pointer_order_span;
750 if (j != tavs.height_list.end()) {
754 temp_pointer_order_span++;
758 /* find out where we'll be when we move and set height accordingly */
760 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
761 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
762 rv->set_height (temp_rtv->view()->child_height());
764 /* if you un-comment the following, the region colours will follow
765 the track colours whilst dragging; personally
766 i think this can confuse things, but never mind.
769 //const GdkColor& col (temp_rtv->view->get_region_color());
770 //rv->set_color (const_cast<GdkColor&>(col));
774 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
776 /* INTER-LAYER MOVEMENT in the same track */
777 y_delta = rtv->view()->child_height () * pointer_layer_span;
781 _editor->mouse_brush_insert_region (rv, pending_region_position);
783 rv->move (x_delta, y_delta);
786 } /* foreach region */
788 _total_x_delta += x_delta;
791 _editor->cursor_group->raise_to_top();
794 if (x_delta != 0 && !_brushing) {
795 _editor->show_verbose_time_cursor (_last_frame_position, 10);
800 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
802 if (_copy && first_move) {
803 copy_regions (event);
806 RegionMotionDrag::motion (event, first_move);
810 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
812 if (!movement_occurred) {
817 /* reverse this here so that we have the correct logic to finalize
821 if (Config->get_edit_mode() == Lock) {
822 _x_constrained = !_x_constrained;
825 bool const changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
826 bool const changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
827 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
829 _editor->update_canvas_now ();
834 find_time_axis_views_and_layers (),
843 find_time_axis_views_and_layers (),
853 RegionMoveDrag::finished_copy (
854 map<RegionView*, pair<RouteTimeAxisView*, int> > const & final,
855 bool const changed_position,
856 bool const changed_tracks,
857 framecnt_t const drag_delta
860 RegionSelection new_views;
861 PlaylistSet modified_playlists;
862 list<RegionView*> views_to_delete;
865 /* all changes were made during motion event handlers */
867 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
871 _editor->commit_reversible_command ();
875 if (_x_constrained) {
876 _editor->begin_reversible_command (_("fixed time region copy"));
878 _editor->begin_reversible_command (_("region copy"));
881 /* insert the regions into their new playlists */
882 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
886 if (i->view->region()->locked()) {
890 if (changed_position && !_x_constrained) {
891 where = i->view->region()->position() - drag_delta;
893 where = i->view->region()->position();
896 map<RegionView*, pair<RouteTimeAxisView*, int> >::const_iterator j = final.find (i->view);
897 assert (j != final.end());
899 RegionView* new_view = insert_region_into_playlist (
900 i->view->region(), j->second.first, j->second.second, where, modified_playlists
907 new_views.push_back (new_view);
909 /* we don't need the copied RegionView any more */
910 views_to_delete.push_back (i->view);
913 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
914 because when views are deleted they are automagically removed from _views, which messes
917 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
921 /* If we've created new regions either by copying or moving
922 to a new track, we want to replace the old selection with the new ones
925 if (new_views.size() > 0) {
926 _editor->selection->set (new_views);
929 /* write commands for the accumulated diffs for all our modified playlists */
930 add_stateful_diff_commands_for_playlists (modified_playlists);
932 _editor->commit_reversible_command ();
936 RegionMoveDrag::finished_no_copy (
937 map<RegionView*, pair<RouteTimeAxisView*, int> > const & final,
938 bool const changed_position,
939 bool const changed_tracks,
940 framecnt_t const drag_delta
943 RegionSelection new_views;
944 PlaylistSet modified_playlists;
945 PlaylistSet frozen_playlists;
948 /* all changes were made during motion event handlers */
949 _editor->commit_reversible_command ();
953 if (_x_constrained) {
954 _editor->begin_reversible_command (_("fixed time region drag"));
956 _editor->begin_reversible_command (_("region drag"));
959 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
961 RegionView* rv = i->view;
963 map<RegionView*, pair<RouteTimeAxisView*, int> >::const_iterator j = final.find (rv);
964 assert (j != final.end());
966 RouteTimeAxisView* dest_rtv = j->second.first;
967 layer_t dest_layer = j->second.second;
969 if (rv->region()->locked()) {
976 if (changed_position && !_x_constrained) {
977 where = rv->region()->position() - drag_delta;
979 where = rv->region()->position();
982 if (changed_tracks) {
984 /* insert into new playlist */
986 RegionView* new_view = insert_region_into_playlist (
987 RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
995 new_views.push_back (new_view);
997 /* remove from old playlist */
999 /* the region that used to be in the old playlist is not
1000 moved to the new one - we use a copy of it. as a result,
1001 any existing editor for the region should no longer be
1004 rv->hide_region_editor();
1005 rv->fake_set_opaque (false);
1007 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1011 rv->region()->clear_changes ();
1014 motion on the same track. plonk the previously reparented region
1015 back to its original canvas group (its streamview).
1016 No need to do anything for copies as they are fake regions which will be deleted.
1019 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1020 rv->get_canvas_group()->property_y() = i->initial_y;
1021 rv->get_time_axis_view().reveal_dependent_views (*rv);
1023 /* just change the model */
1025 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1027 if (dest_rtv->view()->layer_display() == Stacked) {
1028 rv->region()->set_layer (dest_layer);
1029 rv->region()->set_pending_explicit_relayer (true);
1032 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1034 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1037 playlist->freeze ();
1040 /* this movement may result in a crossfade being modified, so we need to get undo
1041 data from the playlist as well as the region.
1044 r = modified_playlists.insert (playlist);
1046 playlist->clear_changes ();
1049 rv->region()->set_position (where, (void*) this);
1051 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1054 if (changed_tracks) {
1056 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1057 was selected in all of them, then removing it from a playlist will have removed all
1058 trace of it from _views (i.e. there were N regions selected, we removed 1,
1059 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1060 corresponding regionview, and _views is now empty).
1062 This could have invalidated any and all iterators into _views.
1064 The heuristic we use here is: if the region selection is empty, break out of the loop
1065 here. if the region selection is not empty, then restart the loop because we know that
1066 we must have removed at least the region(view) we've just been working on as well as any
1067 that we processed on previous iterations.
1069 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1070 we can just iterate.
1074 if (_views.empty()) {
1085 /* If we've created new regions either by copying or moving
1086 to a new track, we want to replace the old selection with the new ones
1089 if (new_views.size() > 0) {
1090 _editor->selection->set (new_views);
1093 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1097 /* write commands for the accumulated diffs for all our modified playlists */
1098 add_stateful_diff_commands_for_playlists (modified_playlists);
1100 _editor->commit_reversible_command ();
1103 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1104 * @param region Region to remove.
1105 * @param playlist playlist To remove from.
1106 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1107 * that clear_changes () is only called once per playlist.
1110 RegionMoveDrag::remove_region_from_playlist (
1111 boost::shared_ptr<Region> region,
1112 boost::shared_ptr<Playlist> playlist,
1113 PlaylistSet& modified_playlists
1116 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1119 playlist->clear_changes ();
1122 playlist->remove_region (region);
1126 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1127 * clearing the playlist's diff history first if necessary.
1128 * @param region Region to insert.
1129 * @param dest_rtv Destination RouteTimeAxisView.
1130 * @param dest_layer Destination layer.
1131 * @param where Destination position.
1132 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1133 * that clear_changes () is only called once per playlist.
1134 * @return New RegionView, or 0 if no insert was performed.
1137 RegionMoveDrag::insert_region_into_playlist (
1138 boost::shared_ptr<Region> region,
1139 RouteTimeAxisView* dest_rtv,
1142 PlaylistSet& modified_playlists
1145 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1146 if (!dest_playlist) {
1150 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1151 _new_region_view = 0;
1152 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1154 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1155 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1157 dest_playlist->clear_changes ();
1160 dest_playlist->add_region (region, where);
1162 if (dest_rtv->view()->layer_display() == Stacked) {
1163 region->set_layer (dest_layer);
1164 region->set_pending_explicit_relayer (true);
1169 assert (_new_region_view);
1171 return _new_region_view;
1175 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1177 _new_region_view = rv;
1181 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1183 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1184 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1186 _editor->session()->add_command (new StatefulDiffCommand (*i));
1195 RegionMoveDrag::aborted ()
1199 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1206 RegionMotionDrag::aborted ();
1211 RegionMotionDrag::aborted ()
1213 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1214 RegionView* rv = i->view;
1215 TimeAxisView* tv = &(rv->get_time_axis_view ());
1216 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1218 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1219 rv->get_canvas_group()->property_y() = 0;
1220 rv->get_time_axis_view().reveal_dependent_views (*rv);
1221 rv->fake_set_opaque (false);
1222 rv->move (-_total_x_delta, 0);
1223 rv->set_height (rtv->view()->child_height ());
1226 _editor->update_canvas_now ();
1231 RegionMotionDrag::x_move_allowed () const
1233 if (Config->get_edit_mode() == Lock) {
1234 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1235 return _x_constrained;
1238 return !_x_constrained;
1242 RegionMotionDrag::copy_regions (GdkEvent* event)
1244 /* duplicate the regionview(s) and region(s) */
1246 list<DraggingView> new_regionviews;
1248 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1250 RegionView* rv = i->view;
1251 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1252 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1254 const boost::shared_ptr<const Region> original = rv->region();
1255 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1256 region_copy->set_position (original->position(), this);
1260 boost::shared_ptr<AudioRegion> audioregion_copy
1261 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1263 nrv = new AudioRegionView (*arv, audioregion_copy);
1265 boost::shared_ptr<MidiRegion> midiregion_copy
1266 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1267 nrv = new MidiRegionView (*mrv, midiregion_copy);
1272 nrv->get_canvas_group()->show ();
1273 new_regionviews.push_back (DraggingView (nrv));
1275 /* swap _primary to the copy */
1277 if (rv == _primary) {
1281 /* ..and deselect the one we copied */
1283 rv->set_selected (false);
1286 if (new_regionviews.empty()) {
1290 /* reflect the fact that we are dragging the copies */
1292 _views = new_regionviews;
1294 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1297 sync the canvas to what we think is its current state
1298 without it, the canvas seems to
1299 "forget" to update properly after the upcoming reparent()
1300 ..only if the mouse is in rapid motion at the time of the grab.
1301 something to do with regionview creation taking so long?
1303 _editor->update_canvas_now();
1307 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1309 /* Which trackview is this ? */
1311 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1312 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1313 (*layer) = tvp.second;
1315 if (*tv && (*tv)->layer_display() == Overlaid) {
1319 /* The region motion is only processed if the pointer is over
1323 if (!(*tv) || !(*tv)->is_track()) {
1324 /* To make sure we hide the verbose canvas cursor when the mouse is
1325 not held over and audiotrack.
1327 _editor->hide_verbose_canvas_cursor ();
1334 /** @param new_order New track order.
1335 * @param old_order Old track order.
1336 * @param visible_y_low Lowest visible order.
1337 * @return true if y movement should not happen, otherwise false.
1340 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1342 if (new_order != old_order) {
1344 /* this isn't the pointer track */
1348 /* moving up the canvas */
1349 if ( (new_order - y_span) >= tavs.visible_y_low) {
1353 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1354 int32_t visible_tracks = 0;
1355 while (visible_tracks < y_span ) {
1357 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1358 /* passing through a hidden track */
1363 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1364 /* moving to a non-track; disallow */
1370 /* moving beyond the lowest visible track; disallow */
1374 } else if (y_span < 0) {
1376 /* moving down the canvas */
1377 if ((new_order - y_span) <= tavs.visible_y_high) {
1379 int32_t visible_tracks = 0;
1381 while (visible_tracks > y_span ) {
1384 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1385 /* passing through a hidden track */
1390 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1391 /* moving to a non-track; disallow */
1398 /* moving beyond the highest visible track; disallow */
1405 /* this is the pointer's track */
1407 if ((new_order - y_span) > tavs.visible_y_high) {
1408 /* we will overflow */
1410 } else if ((new_order - y_span) < tavs.visible_y_low) {
1411 /* we will overflow */
1420 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1421 : RegionMotionDrag (e, i, p, v, b),
1424 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1426 _dest_trackview = tv;
1427 if (tv->layer_display() == Overlaid) {
1430 _dest_layer = _primary->region()->layer ();
1434 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1435 if (rtv && rtv->is_track()) {
1436 speed = rtv->track()->speed ();
1439 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1443 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1445 RegionMotionDrag::start_grab (event, c);
1447 _pointer_frame_offset = grab_frame() - _last_frame_position;
1450 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1451 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1453 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1454 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1456 _primary = v->view()->create_region_view (r, false, false);
1458 _primary->get_canvas_group()->show ();
1459 _primary->set_position (pos, 0);
1460 _views.push_back (DraggingView (_primary));
1462 _last_frame_position = pos;
1464 _item = _primary->get_canvas_group ();
1465 _dest_trackview = v;
1466 _dest_layer = _primary->region()->layer ();
1469 map<RegionView*, pair<RouteTimeAxisView*, int> >
1470 RegionMotionDrag::find_time_axis_views_and_layers ()
1472 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1474 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1476 double ix1, ix2, iy1, iy2;
1477 RegionView* rv = i->view;
1478 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1479 rv->get_canvas_frame()->i2w (ix1, iy1);
1480 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1482 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1483 tav[rv] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1491 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1493 _editor->update_canvas_now ();
1495 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1497 RouteTimeAxisView* dest_rtv = final[_primary].first;
1499 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1500 _primary->get_canvas_group()->property_y() = 0;
1502 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1504 _editor->begin_reversible_command (_("insert region"));
1505 playlist->clear_changes ();
1506 playlist->add_region (_primary->region (), _last_frame_position);
1507 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1508 _editor->commit_reversible_command ();
1516 RegionInsertDrag::aborted ()
1523 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1524 : RegionMoveDrag (e, i, p, v, false, false)
1529 struct RegionSelectionByPosition {
1530 bool operator() (RegionView*a, RegionView* b) {
1531 return a->region()->position () < b->region()->position();
1536 RegionSpliceDrag::motion (GdkEvent* event, bool)
1538 RouteTimeAxisView* tv;
1541 if (!check_possible (&tv, &layer)) {
1547 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1553 RegionSelection copy (_editor->selection->regions);
1555 RegionSelectionByPosition cmp;
1558 nframes64_t const pf = adjusted_current_frame (event);
1560 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1562 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1568 boost::shared_ptr<Playlist> playlist;
1570 if ((playlist = atv->playlist()) == 0) {
1574 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1579 if (pf < (*i)->region()->last_frame() + 1) {
1583 if (pf > (*i)->region()->first_frame()) {
1589 playlist->shuffle ((*i)->region(), dir);
1594 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1600 RegionSpliceDrag::aborted ()
1605 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1607 _view (dynamic_cast<MidiTimeAxisView*> (v))
1613 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1616 /* don't use a zero-length region otherwise its region view will be hidden when it is created */
1617 _region = _view->add_region (grab_frame(), 1, false);
1619 framepos_t const f = adjusted_current_frame (event);
1620 if (f < grab_frame()) {
1621 _region->set_position (f, this);
1624 /* again, don't use a zero-length region (see above) */
1625 framecnt_t const len = abs (f - grab_frame ());
1626 _region->set_length (len < 1 ? 1 : len, this);
1631 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1633 if (movement_occurred) {
1634 _editor->commit_reversible_command ();
1639 RegionCreateDrag::aborted ()
1644 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1652 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1654 Gdk::Cursor* cursor;
1655 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1657 Drag::start_grab (event);
1659 region = &cnote->region_view();
1661 double const region_start = region->get_position_pixels();
1662 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1664 if (grab_x() <= middle_point) {
1665 cursor = _editor->left_side_trim_cursor;
1668 cursor = _editor->right_side_trim_cursor;
1672 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1674 if (event->motion.state & Keyboard::PrimaryModifier) {
1680 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1682 if (ms.size() > 1) {
1683 /* has to be relative, may make no sense otherwise */
1687 /* select this note; if it is already selected, preserve the existing selection,
1688 otherwise make this note the only one selected.
1690 region->note_selected (cnote, cnote->selected ());
1692 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1693 MidiRegionSelection::iterator next;
1696 (*r)->begin_resizing (at_front);
1702 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1704 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1705 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1706 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1711 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1713 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1714 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1715 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1720 NoteResizeDrag::aborted ()
1726 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1732 RegionGainDrag::finished (GdkEvent *, bool)
1738 RegionGainDrag::aborted ()
1743 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1744 : RegionDrag (e, i, p, v)
1745 , _have_transaction (false)
1751 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1754 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1755 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1757 if (tv && tv->is_track()) {
1758 speed = tv->track()->speed();
1761 nframes64_t const region_start = (nframes64_t) (_primary->region()->position() / speed);
1762 nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1763 nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1765 nframes64_t const pf = adjusted_current_frame (event);
1767 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1768 _operation = ContentsTrim;
1769 Drag::start_grab (event, _editor->trimmer_cursor);
1771 /* These will get overridden for a point trim.*/
1772 if (pf < (region_start + region_length/2)) {
1773 /* closer to start */
1774 _operation = StartTrim;
1775 Drag::start_grab (event, _editor->left_side_trim_cursor);
1778 _operation = EndTrim;
1779 Drag::start_grab (event, _editor->right_side_trim_cursor);
1783 switch (_operation) {
1785 _editor->show_verbose_time_cursor (region_start, 10);
1788 _editor->show_verbose_time_cursor (region_end, 10);
1791 _editor->show_verbose_time_cursor (pf, 10);
1797 TrimDrag::motion (GdkEvent* event, bool first_move)
1799 RegionView* rv = _primary;
1801 /* snap modifier works differently here..
1802 its current state has to be passed to the
1803 various trim functions in order to work properly
1807 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1808 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1809 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1811 if (tv && tv->is_track()) {
1812 speed = tv->track()->speed();
1815 nframes64_t const pf = adjusted_current_frame (event);
1821 switch (_operation) {
1823 trim_type = "Region start trim";
1826 trim_type = "Region end trim";
1829 trim_type = "Region content trim";
1833 _editor->begin_reversible_command (trim_type);
1834 _have_transaction = true;
1836 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1837 RegionView* rv = i->view;
1838 rv->fake_set_opaque(false);
1839 rv->enable_display (false);
1840 rv->region()->clear_changes ();
1841 rv->region()->suspend_property_changes ();
1843 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1846 arv->temporarily_hide_envelope ();
1849 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1850 insert_result = _editor->motion_frozen_playlists.insert (pl);
1852 if (insert_result.second) {
1858 bool non_overlap_trim = false;
1860 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1861 non_overlap_trim = true;
1864 switch (_operation) {
1866 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1867 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1872 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1873 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1879 bool swap_direction = false;
1881 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1882 swap_direction = true;
1885 nframes64_t frame_delta = 0;
1887 bool left_direction = false;
1888 if (last_pointer_frame() > pf) {
1889 left_direction = true;
1892 if (left_direction) {
1893 frame_delta = (last_pointer_frame() - pf);
1895 frame_delta = (pf - last_pointer_frame());
1898 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1899 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1905 switch (_operation) {
1907 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1910 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1913 _editor->show_verbose_time_cursor (pf, 10);
1920 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1922 if (movement_occurred) {
1923 motion (event, false);
1925 if (!_editor->selection->selected (_primary)) {
1926 _editor->thaw_region_after_trim (*_primary);
1929 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1930 _editor->thaw_region_after_trim (*i->view);
1931 i->view->enable_display (true);
1932 i->view->fake_set_opaque (true);
1933 if (_have_transaction) {
1934 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1938 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1942 _editor->motion_frozen_playlists.clear ();
1944 if (_have_transaction) {
1945 _editor->commit_reversible_command();
1949 /* no mouse movement */
1950 _editor->point_trim (event, adjusted_current_frame (event));
1955 TrimDrag::aborted ()
1957 /* Our motion method is changing model state, so use the Undo system
1958 to cancel. Perhaps not ideal, as this will leave an Undo point
1959 behind which may be slightly odd from the user's point of view.
1964 if (_have_transaction) {
1969 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1973 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1978 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1981 // create a dummy marker for visual representation of moving the copy.
1982 // The actual copying is not done before we reach the finish callback.
1984 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1985 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1986 *new MeterSection (_marker->meter()));
1988 _item = &new_marker->the_item ();
1989 _marker = new_marker;
1993 MetricSection& section (_marker->meter());
1995 if (!section.movable()) {
2001 Drag::start_grab (event, cursor);
2003 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
2005 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
2009 MeterMarkerDrag::motion (GdkEvent* event, bool)
2011 nframes64_t const pf = adjusted_current_frame (event);
2013 _marker->set_position (pf);
2015 _editor->show_verbose_time_cursor (pf, 10);
2019 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2021 if (!movement_occurred) {
2025 motion (event, false);
2029 TempoMap& map (_editor->session()->tempo_map());
2030 map.bbt_time (last_pointer_frame(), when);
2032 if (_copy == true) {
2033 _editor->begin_reversible_command (_("copy meter mark"));
2034 XMLNode &before = map.get_state();
2035 map.add_meter (_marker->meter(), when);
2036 XMLNode &after = map.get_state();
2037 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2038 _editor->commit_reversible_command ();
2040 // delete the dummy marker we used for visual representation of copying.
2041 // a new visual marker will show up automatically.
2044 _editor->begin_reversible_command (_("move meter mark"));
2045 XMLNode &before = map.get_state();
2046 map.move_meter (_marker->meter(), when);
2047 XMLNode &after = map.get_state();
2048 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2049 _editor->commit_reversible_command ();
2054 MeterMarkerDrag::aborted ()
2056 _marker->set_position (_marker->meter().frame ());
2059 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2063 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2068 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2073 // create a dummy marker for visual representation of moving the copy.
2074 // The actual copying is not done before we reach the finish callback.
2076 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2077 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2078 *new TempoSection (_marker->tempo()));
2080 _item = &new_marker->the_item ();
2081 _marker = new_marker;
2085 MetricSection& section (_marker->tempo());
2087 if (!section.movable()) {
2092 Drag::start_grab (event, cursor);
2094 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2095 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2099 TempoMarkerDrag::motion (GdkEvent* event, bool)
2101 nframes64_t const pf = adjusted_current_frame (event);
2102 _marker->set_position (pf);
2103 _editor->show_verbose_time_cursor (pf, 10);
2107 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2109 if (!movement_occurred) {
2113 motion (event, false);
2117 TempoMap& map (_editor->session()->tempo_map());
2118 map.bbt_time (last_pointer_frame(), when);
2120 if (_copy == true) {
2121 _editor->begin_reversible_command (_("copy tempo mark"));
2122 XMLNode &before = map.get_state();
2123 map.add_tempo (_marker->tempo(), when);
2124 XMLNode &after = map.get_state();
2125 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2126 _editor->commit_reversible_command ();
2128 // delete the dummy marker we used for visual representation of copying.
2129 // a new visual marker will show up automatically.
2132 _editor->begin_reversible_command (_("move tempo mark"));
2133 XMLNode &before = map.get_state();
2134 map.move_tempo (_marker->tempo(), when);
2135 XMLNode &after = map.get_state();
2136 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2137 _editor->commit_reversible_command ();
2142 TempoMarkerDrag::aborted ()
2144 _marker->set_position (_marker->tempo().frame());
2147 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2151 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2156 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2158 Drag::start_grab (event, c);
2162 nframes64_t where = _editor->event_frame (event, 0, 0);
2164 _editor->snap_to_with_modifier (where, event);
2165 _editor->playhead_cursor->set_position (where);
2169 if (_cursor == _editor->playhead_cursor) {
2170 _editor->_dragging_playhead = true;
2172 Session* s = _editor->session ();
2175 if (_was_rolling && _stop) {
2179 if (s->is_auditioning()) {
2180 s->cancel_audition ();
2183 s->request_suspend_timecode_transmission ();
2185 if (s->timecode_transmission_suspended ()) {
2186 nframes64_t const f = _editor->playhead_cursor->current_frame;
2187 s->send_mmc_locate (f);
2188 s->send_full_time_code (f);
2193 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2195 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2199 CursorDrag::motion (GdkEvent* event, bool)
2201 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2203 if (adjusted_frame == last_pointer_frame()) {
2207 _cursor->set_position (adjusted_frame);
2209 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2211 Session* s = _editor->session ();
2212 if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2213 nframes64_t const f = _editor->playhead_cursor->current_frame;
2214 s->send_mmc_locate (f);
2215 s->send_full_time_code (f);
2220 _editor->update_canvas_now ();
2222 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2226 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2228 _editor->_dragging_playhead = false;
2230 if (!movement_occurred && _stop) {
2234 motion (event, false);
2236 if (_item == &_editor->playhead_cursor->canvas_item) {
2237 Session* s = _editor->session ();
2239 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2240 _editor->_pending_locate_request = true;
2241 s->request_resume_timecode_transmission ();
2247 CursorDrag::aborted ()
2249 if (_editor->_dragging_playhead) {
2250 _editor->session()->request_resume_timecode_transmission ();
2251 _editor->_dragging_playhead = false;
2254 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2257 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2258 : RegionDrag (e, i, p, v)
2264 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2266 Drag::start_grab (event, cursor);
2268 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2269 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2271 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2272 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2277 FadeInDrag::motion (GdkEvent* event, bool)
2279 nframes64_t fade_length;
2281 nframes64_t const pos = adjusted_current_frame (event);
2283 boost::shared_ptr<Region> region = _primary->region ();
2285 if (pos < (region->position() + 64)) {
2286 fade_length = 64; // this should be a minimum defined somewhere
2287 } else if (pos > region->last_frame()) {
2288 fade_length = region->length();
2290 fade_length = pos - region->position();
2293 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2295 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2301 tmp->reset_fade_in_shape_width (fade_length);
2304 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2308 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2310 if (!movement_occurred) {
2314 nframes64_t fade_length;
2316 nframes64_t const pos = adjusted_current_frame (event);
2318 boost::shared_ptr<Region> region = _primary->region ();
2320 if (pos < (region->position() + 64)) {
2321 fade_length = 64; // this should be a minimum defined somewhere
2322 } else if (pos > region->last_frame()) {
2323 fade_length = region->length();
2325 fade_length = pos - region->position();
2328 _editor->begin_reversible_command (_("change fade in length"));
2330 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2332 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2338 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2339 XMLNode &before = alist->get_state();
2341 tmp->audio_region()->set_fade_in_length (fade_length);
2342 tmp->audio_region()->set_fade_in_active (true);
2344 XMLNode &after = alist->get_state();
2345 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2348 _editor->commit_reversible_command ();
2352 FadeInDrag::aborted ()
2354 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2355 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2361 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2365 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2366 : RegionDrag (e, i, p, v)
2372 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2374 Drag::start_grab (event, cursor);
2376 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2377 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2379 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2380 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2384 FadeOutDrag::motion (GdkEvent* event, bool)
2386 nframes64_t fade_length;
2388 nframes64_t const pos = adjusted_current_frame (event);
2390 boost::shared_ptr<Region> region = _primary->region ();
2392 if (pos > (region->last_frame() - 64)) {
2393 fade_length = 64; // this should really be a minimum fade defined somewhere
2395 else if (pos < region->position()) {
2396 fade_length = region->length();
2399 fade_length = region->last_frame() - pos;
2402 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2404 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2410 tmp->reset_fade_out_shape_width (fade_length);
2413 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2417 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2419 if (!movement_occurred) {
2423 nframes64_t fade_length;
2425 nframes64_t const pos = adjusted_current_frame (event);
2427 boost::shared_ptr<Region> region = _primary->region ();
2429 if (pos > (region->last_frame() - 64)) {
2430 fade_length = 64; // this should really be a minimum fade defined somewhere
2432 else if (pos < region->position()) {
2433 fade_length = region->length();
2436 fade_length = region->last_frame() - pos;
2439 _editor->begin_reversible_command (_("change fade out length"));
2441 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2443 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2449 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2450 XMLNode &before = alist->get_state();
2452 tmp->audio_region()->set_fade_out_length (fade_length);
2453 tmp->audio_region()->set_fade_out_active (true);
2455 XMLNode &after = alist->get_state();
2456 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2459 _editor->commit_reversible_command ();
2463 FadeOutDrag::aborted ()
2465 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2466 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2472 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2476 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2479 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2482 _points.push_back (Gnome::Art::Point (0, 0));
2483 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2485 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2486 _line->property_width_pixels() = 1;
2487 _line->property_points () = _points;
2490 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2493 MarkerDrag::~MarkerDrag ()
2495 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2501 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2503 Drag::start_grab (event, cursor);
2507 Location *location = _editor->find_location_from_marker (_marker, is_start);
2508 _editor->_dragging_edit_point = true;
2510 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2512 update_item (location);
2514 // _drag_line->show();
2515 // _line->raise_to_top();
2518 _editor->show_verbose_time_cursor (location->start(), 10);
2520 _editor->show_verbose_time_cursor (location->end(), 10);
2523 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2526 case Selection::Toggle:
2527 _editor->selection->toggle (_marker);
2529 case Selection::Set:
2530 if (!_editor->selection->selected (_marker)) {
2531 _editor->selection->set (_marker);
2534 case Selection::Extend:
2536 Locations::LocationList ll;
2537 list<Marker*> to_add;
2539 _editor->selection->markers.range (s, e);
2540 s = min (_marker->position(), s);
2541 e = max (_marker->position(), e);
2544 if (e < max_frames) {
2547 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2548 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2549 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2552 to_add.push_back (lm->start);
2555 to_add.push_back (lm->end);
2559 if (!to_add.empty()) {
2560 _editor->selection->add (to_add);
2564 case Selection::Add:
2565 _editor->selection->add (_marker);
2569 /* Set up copies for us to manipulate during the drag */
2571 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2572 Location* l = _editor->find_location_from_marker (*i, is_start);
2573 _copied_locations.push_back (new Location (*l));
2578 MarkerDrag::motion (GdkEvent* event, bool)
2580 nframes64_t f_delta = 0;
2582 bool move_both = false;
2584 Location *real_location;
2585 Location *copy_location = 0;
2587 nframes64_t const newframe = adjusted_current_frame (event);
2589 nframes64_t next = newframe;
2591 if (newframe == last_pointer_frame()) {
2595 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2599 MarkerSelection::iterator i;
2600 list<Location*>::iterator x;
2602 /* find the marker we're dragging, and compute the delta */
2604 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2605 x != _copied_locations.end() && i != _editor->selection->markers.end();
2611 if (marker == _marker) {
2613 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2618 if (real_location->is_mark()) {
2619 f_delta = newframe - copy_location->start();
2623 switch (marker->type()) {
2625 case Marker::LoopStart:
2626 case Marker::PunchIn:
2627 f_delta = newframe - copy_location->start();
2631 case Marker::LoopEnd:
2632 case Marker::PunchOut:
2633 f_delta = newframe - copy_location->end();
2636 /* what kind of marker is this ? */
2644 if (i == _editor->selection->markers.end()) {
2645 /* hmm, impossible - we didn't find the dragged marker */
2649 /* now move them all */
2651 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2652 x != _copied_locations.end() && i != _editor->selection->markers.end();
2658 /* call this to find out if its the start or end */
2660 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2664 if (real_location->locked()) {
2668 if (copy_location->is_mark()) {
2672 copy_location->set_start (copy_location->start() + f_delta);
2676 nframes64_t new_start = copy_location->start() + f_delta;
2677 nframes64_t new_end = copy_location->end() + f_delta;
2679 if (is_start) { // start-of-range marker
2682 copy_location->set_start (new_start);
2683 copy_location->set_end (new_end);
2684 } else if (new_start < copy_location->end()) {
2685 copy_location->set_start (new_start);
2687 _editor->snap_to (next, 1, true);
2688 copy_location->set_end (next);
2689 copy_location->set_start (newframe);
2692 } else { // end marker
2695 copy_location->set_end (new_end);
2696 copy_location->set_start (new_start);
2697 } else if (new_end > copy_location->start()) {
2698 copy_location->set_end (new_end);
2699 } else if (newframe > 0) {
2700 _editor->snap_to (next, -1, true);
2701 copy_location->set_start (next);
2702 copy_location->set_end (newframe);
2707 update_item (copy_location);
2709 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2712 lm->set_position (copy_location->start(), copy_location->end());
2716 assert (!_copied_locations.empty());
2718 _editor->show_verbose_time_cursor (newframe, 10);
2721 _editor->update_canvas_now ();
2726 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2728 if (!movement_occurred) {
2730 /* just a click, do nothing but finish
2731 off the selection process
2734 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2737 case Selection::Set:
2738 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2739 _editor->selection->set (_marker);
2743 case Selection::Toggle:
2744 case Selection::Extend:
2745 case Selection::Add:
2752 _editor->_dragging_edit_point = false;
2754 _editor->begin_reversible_command ( _("move marker") );
2755 XMLNode &before = _editor->session()->locations()->get_state();
2757 MarkerSelection::iterator i;
2758 list<Location*>::iterator x;
2761 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2762 x != _copied_locations.end() && i != _editor->selection->markers.end();
2765 Location * location = _editor->find_location_from_marker (*i, is_start);
2769 if (location->locked()) {
2773 if (location->is_mark()) {
2774 location->set_start ((*x)->start());
2776 location->set ((*x)->start(), (*x)->end());
2781 XMLNode &after = _editor->session()->locations()->get_state();
2782 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2783 _editor->commit_reversible_command ();
2789 MarkerDrag::aborted ()
2795 MarkerDrag::update_item (Location* location)
2797 double const x1 = _editor->frame_to_pixel (location->start());
2799 _points.front().set_x(x1);
2800 _points.back().set_x(x1);
2801 _line->property_points() = _points;
2804 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2806 _cumulative_x_drag (0),
2807 _cumulative_y_drag (0)
2809 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2815 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2817 Drag::start_grab (event, _editor->fader_cursor);
2819 // start the grab at the center of the control point so
2820 // the point doesn't 'jump' to the mouse after the first drag
2821 _fixed_grab_x = _point->get_x();
2822 _fixed_grab_y = _point->get_y();
2824 float const fraction = 1 - (_point->get_y() / _point->line().height());
2826 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2828 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2829 event->button.x + 10, event->button.y + 10);
2831 _editor->show_verbose_canvas_cursor ();
2835 ControlPointDrag::motion (GdkEvent* event, bool)
2837 double dx = _drags->current_pointer_x() - last_pointer_x();
2838 double dy = _drags->current_pointer_y() - last_pointer_y();
2840 if (event->button.state & Keyboard::SecondaryModifier) {
2845 /* coordinate in pixels relative to the start of the region (for region-based automation)
2846 or track (for track-based automation) */
2847 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2848 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2850 // calculate zero crossing point. back off by .01 to stay on the
2851 // positive side of zero
2852 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2854 // make sure we hit zero when passing through
2855 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2859 if (_x_constrained) {
2862 if (_y_constrained) {
2866 _cumulative_x_drag = cx - _fixed_grab_x;
2867 _cumulative_y_drag = cy - _fixed_grab_y;
2871 cy = min ((double) _point->line().height(), cy);
2873 framepos_t cx_frames = _editor->unit_to_frame (cx);
2875 if (!_x_constrained) {
2876 _editor->snap_to_with_modifier (cx_frames, event);
2879 cx_frames = min (cx_frames, _point->line().maximum_time());
2881 float const fraction = 1.0 - (cy / _point->line().height());
2883 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2885 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2887 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2891 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2893 if (!movement_occurred) {
2897 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2898 _editor->reset_point_selection ();
2902 motion (event, false);
2904 _point->line().end_drag ();
2908 ControlPointDrag::aborted ()
2910 _point->line().reset ();
2914 ControlPointDrag::active (Editing::MouseMode m)
2916 if (m == Editing::MouseGain) {
2917 /* always active in mouse gain */
2921 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2922 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2925 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2928 _cumulative_y_drag (0)
2933 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2935 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2938 _item = &_line->grab_item ();
2940 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2941 origin, and ditto for y.
2944 double cx = event->button.x;
2945 double cy = event->button.y;
2947 _line->parent_group().w2i (cx, cy);
2949 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2954 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2955 /* no adjacent points */
2959 Drag::start_grab (event, _editor->fader_cursor);
2961 /* store grab start in parent frame */
2966 double fraction = 1.0 - (cy / _line->height());
2968 _line->start_drag_line (before, after, fraction);
2970 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2971 event->button.x + 10, event->button.y + 10);
2973 _editor->show_verbose_canvas_cursor ();
2977 LineDrag::motion (GdkEvent* event, bool)
2979 double dy = _drags->current_pointer_y() - last_pointer_y();
2981 if (event->button.state & Keyboard::SecondaryModifier) {
2985 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2987 _cumulative_y_drag = cy - _fixed_grab_y;
2990 cy = min ((double) _line->height(), cy);
2992 double const fraction = 1.0 - (cy / _line->height());
2996 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3002 /* we are ignoring x position for this drag, so we can just pass in anything */
3003 _line->drag_motion (0, fraction, true, push);
3005 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
3009 LineDrag::finished (GdkEvent* event, bool)
3011 motion (event, false);
3016 LineDrag::aborted ()
3021 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3024 _cumulative_x_drag (0)
3029 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3032 Drag::start_grab (event);
3034 _line = reinterpret_cast<SimpleLine*> (_item);
3037 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3039 double cx = event->button.x;
3040 double cy = event->button.y;
3042 _item->property_parent().get_value()->w2i(cx, cy);
3044 /* store grab start in parent frame */
3045 _region_view_grab_x = cx;
3047 _before = _line->property_x1();
3049 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3051 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3055 FeatureLineDrag::motion (GdkEvent* event, bool)
3057 double dx = _drags->current_pointer_x() - last_pointer_x();
3059 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3061 _cumulative_x_drag += dx;
3063 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3072 _line->property_x1() = cx;
3073 _line->property_x2() = cx;
3075 _before = _line->property_x1();
3079 FeatureLineDrag::finished (GdkEvent* event, bool)
3081 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3082 _arv->update_transient(_before, _line->property_x1());
3086 FeatureLineDrag::aborted ()
3092 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3094 Drag::start_grab (event);
3095 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3099 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3106 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3108 nframes64_t grab = grab_frame ();
3109 if (Config->get_rubberbanding_snaps_to_grid ()) {
3110 _editor->snap_to_with_modifier (grab, event);
3113 /* base start and end on initial click position */
3123 if (_drags->current_pointer_y() < grab_y()) {
3124 y1 = _drags->current_pointer_y();
3127 y2 = _drags->current_pointer_y();
3132 if (start != end || y1 != y2) {
3134 double x1 = _editor->frame_to_pixel (start);
3135 double x2 = _editor->frame_to_pixel (end);
3137 _editor->rubberband_rect->property_x1() = x1;
3138 _editor->rubberband_rect->property_y1() = y1;
3139 _editor->rubberband_rect->property_x2() = x2;
3140 _editor->rubberband_rect->property_y2() = y2;
3142 _editor->rubberband_rect->show();
3143 _editor->rubberband_rect->raise_to_top();
3145 _editor->show_verbose_time_cursor (pf, 10);
3150 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3152 if (movement_occurred) {
3154 motion (event, false);
3157 if (_drags->current_pointer_y() < grab_y()) {
3158 y1 = _drags->current_pointer_y();
3161 y2 = _drags->current_pointer_y();
3166 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3169 _editor->begin_reversible_command (_("rubberband selection"));
3171 if (grab_frame() < last_pointer_frame()) {
3172 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3174 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3178 _editor->commit_reversible_command ();
3182 if (!getenv("ARDOUR_SAE")) {
3183 _editor->selection->clear_tracks();
3185 _editor->selection->clear_regions();
3186 _editor->selection->clear_points ();
3187 _editor->selection->clear_lines ();
3190 _editor->rubberband_rect->hide();
3194 RubberbandSelectDrag::aborted ()
3196 _editor->rubberband_rect->hide ();
3200 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3202 Drag::start_grab (event);
3204 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3208 TimeFXDrag::motion (GdkEvent* event, bool)
3210 RegionView* rv = _primary;
3212 nframes64_t const pf = adjusted_current_frame (event);
3214 if (pf > rv->region()->position()) {
3215 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3218 _editor->show_verbose_time_cursor (pf, 10);
3222 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3224 _primary->get_time_axis_view().hide_timestretch ();
3226 if (!movement_occurred) {
3230 if (last_pointer_frame() < _primary->region()->position()) {
3231 /* backwards drag of the left edge - not usable */
3235 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3237 float percentage = (double) newlen / (double) _primary->region()->length();
3239 #ifndef USE_RUBBERBAND
3240 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3241 if (_primary->region()->data_type() == DataType::AUDIO) {
3242 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3246 _editor->begin_reversible_command (_("timestretch"));
3248 // XXX how do timeFX on multiple regions ?
3253 if (_editor->time_stretch (rs, percentage) == -1) {
3254 error << _("An error occurred while executing time stretch operation") << endmsg;
3259 TimeFXDrag::aborted ()
3261 _primary->get_time_axis_view().hide_timestretch ();
3266 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3268 Drag::start_grab (event);
3272 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3274 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3278 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3280 if (movement_occurred && _editor->session()) {
3281 /* make sure we stop */
3282 _editor->session()->request_transport_speed (0.0);
3287 ScrubDrag::aborted ()
3292 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3296 , _original_pointer_time_axis (-1)
3297 , _last_pointer_time_axis (-1)
3303 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3305 nframes64_t start = 0;
3306 nframes64_t end = 0;
3308 if (_editor->session() == 0) {
3312 Gdk::Cursor* cursor = 0;
3314 switch (_operation) {
3315 case CreateSelection:
3316 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3321 cursor = _editor->selector_cursor;
3322 Drag::start_grab (event, cursor);
3325 case SelectionStartTrim:
3326 if (_editor->clicked_axisview) {
3327 _editor->clicked_axisview->order_selection_trims (_item, true);
3329 Drag::start_grab (event, _editor->left_side_trim_cursor);
3330 start = _editor->selection->time[_editor->clicked_selection].start;
3331 _pointer_frame_offset = grab_frame() - start;
3334 case SelectionEndTrim:
3335 if (_editor->clicked_axisview) {
3336 _editor->clicked_axisview->order_selection_trims (_item, false);
3338 Drag::start_grab (event, _editor->right_side_trim_cursor);
3339 end = _editor->selection->time[_editor->clicked_selection].end;
3340 _pointer_frame_offset = grab_frame() - end;
3344 start = _editor->selection->time[_editor->clicked_selection].start;
3345 Drag::start_grab (event, cursor);
3346 _pointer_frame_offset = grab_frame() - start;
3350 if (_operation == SelectionMove) {
3351 _editor->show_verbose_time_cursor (start, 10);
3353 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3356 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3360 SelectionDrag::motion (GdkEvent* event, bool first_move)
3362 nframes64_t start = 0;
3363 nframes64_t end = 0;
3366 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3367 if (pending_time_axis.first == 0) {
3371 nframes64_t const pending_position = adjusted_current_frame (event);
3373 /* only alter selection if things have changed */
3375 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3379 switch (_operation) {
3380 case CreateSelection:
3382 nframes64_t grab = grab_frame ();
3385 _editor->snap_to (grab);
3388 if (pending_position < grab_frame()) {
3389 start = pending_position;
3392 end = pending_position;
3396 /* first drag: Either add to the selection
3397 or create a new selection
3403 /* adding to the selection */
3404 _editor->selection->add (_editor->clicked_axisview);
3405 _editor->clicked_selection = _editor->selection->add (start, end);
3410 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3411 _editor->selection->set (_editor->clicked_axisview);
3414 _editor->clicked_selection = _editor->selection->set (start, end);
3418 /* select the track that we're in */
3419 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3420 _editor->selection->add (pending_time_axis.first);
3421 _added_time_axes.push_back (pending_time_axis.first);
3424 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3425 tracks that we selected in the first place.
3428 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3429 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3431 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3432 while (i != _added_time_axes.end()) {
3434 list<TimeAxisView*>::iterator tmp = i;
3437 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3438 _editor->selection->remove (*i);
3439 _added_time_axes.remove (*i);
3448 case SelectionStartTrim:
3450 start = _editor->selection->time[_editor->clicked_selection].start;
3451 end = _editor->selection->time[_editor->clicked_selection].end;
3453 if (pending_position > end) {
3456 start = pending_position;
3460 case SelectionEndTrim:
3462 start = _editor->selection->time[_editor->clicked_selection].start;
3463 end = _editor->selection->time[_editor->clicked_selection].end;
3465 if (pending_position < start) {
3468 end = pending_position;
3475 start = _editor->selection->time[_editor->clicked_selection].start;
3476 end = _editor->selection->time[_editor->clicked_selection].end;
3478 length = end - start;
3480 start = pending_position;
3481 _editor->snap_to (start);
3483 end = start + length;
3488 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3489 _editor->start_canvas_autoscroll (1, 0);
3493 _editor->selection->replace (_editor->clicked_selection, start, end);
3496 if (_operation == SelectionMove) {
3497 _editor->show_verbose_time_cursor(start, 10);
3499 _editor->show_verbose_time_cursor(pending_position, 10);
3504 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3506 Session* s = _editor->session();
3508 if (movement_occurred) {
3509 motion (event, false);
3510 /* XXX this is not object-oriented programming at all. ick */
3511 if (_editor->selection->time.consolidate()) {
3512 _editor->selection->TimeChanged ();
3515 /* XXX what if its a music time selection? */
3516 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3517 s->request_play_range (&_editor->selection->time, true);
3522 /* just a click, no pointer movement.*/
3524 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3525 _editor->selection->clear_time();
3528 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3529 _editor->selection->set (_editor->clicked_axisview);
3532 if (s && s->get_play_range () && s->transport_rolling()) {
3533 s->request_stop (false, false);
3538 _editor->stop_canvas_autoscroll ();
3542 SelectionDrag::aborted ()
3547 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3552 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3553 physical_screen_height (_editor->get_window()));
3554 _drag_rect->hide ();
3556 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3557 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3561 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3563 if (_editor->session() == 0) {
3567 Gdk::Cursor* cursor = 0;
3569 if (!_editor->temp_location) {
3570 _editor->temp_location = new Location (*_editor->session());
3573 switch (_operation) {
3574 case CreateRangeMarker:
3575 case CreateTransportMarker:
3576 case CreateCDMarker:
3578 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3583 cursor = _editor->selector_cursor;
3587 Drag::start_grab (event, cursor);
3589 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3593 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3595 nframes64_t start = 0;
3596 nframes64_t end = 0;
3597 ArdourCanvas::SimpleRect *crect;
3599 switch (_operation) {
3600 case CreateRangeMarker:
3601 crect = _editor->range_bar_drag_rect;
3603 case CreateTransportMarker:
3604 crect = _editor->transport_bar_drag_rect;
3606 case CreateCDMarker:
3607 crect = _editor->cd_marker_bar_drag_rect;
3610 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3615 nframes64_t const pf = adjusted_current_frame (event);
3617 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3618 nframes64_t grab = grab_frame ();
3619 _editor->snap_to (grab);
3621 if (pf < grab_frame()) {
3629 /* first drag: Either add to the selection
3630 or create a new selection.
3635 _editor->temp_location->set (start, end);
3639 update_item (_editor->temp_location);
3641 //_drag_rect->raise_to_top();
3646 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3647 _editor->start_canvas_autoscroll (1, 0);
3651 _editor->temp_location->set (start, end);
3653 double x1 = _editor->frame_to_pixel (start);
3654 double x2 = _editor->frame_to_pixel (end);
3655 crect->property_x1() = x1;
3656 crect->property_x2() = x2;
3658 update_item (_editor->temp_location);
3661 _editor->show_verbose_time_cursor (pf, 10);
3666 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3668 Location * newloc = 0;
3672 if (movement_occurred) {
3673 motion (event, false);
3676 switch (_operation) {
3677 case CreateRangeMarker:
3678 case CreateCDMarker:
3680 _editor->begin_reversible_command (_("new range marker"));
3681 XMLNode &before = _editor->session()->locations()->get_state();
3682 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3683 if (_operation == CreateCDMarker) {
3684 flags = Location::IsRangeMarker | Location::IsCDMarker;
3685 _editor->cd_marker_bar_drag_rect->hide();
3688 flags = Location::IsRangeMarker;
3689 _editor->range_bar_drag_rect->hide();
3691 newloc = new Location (
3692 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3695 _editor->session()->locations()->add (newloc, true);
3696 XMLNode &after = _editor->session()->locations()->get_state();
3697 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3698 _editor->commit_reversible_command ();
3702 case CreateTransportMarker:
3703 // popup menu to pick loop or punch
3704 _editor->new_transport_marker_context_menu (&event->button, _item);
3708 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3710 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3715 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3717 if (end == max_frames) {
3718 end = _editor->session()->current_end_frame ();
3721 if (start == max_frames) {
3722 start = _editor->session()->current_start_frame ();
3725 switch (_editor->mouse_mode) {
3727 /* find the two markers on either side and then make the selection from it */
3728 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3732 /* find the two markers on either side of the click and make the range out of it */
3733 _editor->selection->set (start, end);
3742 _editor->stop_canvas_autoscroll ();
3746 RangeMarkerBarDrag::aborted ()
3752 RangeMarkerBarDrag::update_item (Location* location)
3754 double const x1 = _editor->frame_to_pixel (location->start());
3755 double const x2 = _editor->frame_to_pixel (location->end());
3757 _drag_rect->property_x1() = x1;
3758 _drag_rect->property_x2() = x2;
3762 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3764 Drag::start_grab (event, _editor->zoom_cursor);
3765 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3769 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3774 nframes64_t const pf = adjusted_current_frame (event);
3776 nframes64_t grab = grab_frame ();
3777 _editor->snap_to_with_modifier (grab, event);
3779 /* base start and end on initial click position */
3791 _editor->zoom_rect->show();
3792 _editor->zoom_rect->raise_to_top();
3795 _editor->reposition_zoom_rect(start, end);
3797 _editor->show_verbose_time_cursor (pf, 10);
3802 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3804 if (movement_occurred) {
3805 motion (event, false);
3807 if (grab_frame() < last_pointer_frame()) {
3808 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3810 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3813 _editor->temporal_zoom_to_frame (false, grab_frame());
3815 temporal_zoom_step (false);
3816 center_screen (grab_frame());
3820 _editor->zoom_rect->hide();
3824 MouseZoomDrag::aborted ()
3826 _editor->zoom_rect->hide ();
3829 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3831 , _cumulative_dx (0)
3832 , _cumulative_dy (0)
3834 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3835 _region = &_primary->region_view ();
3836 _note_height = _region->midi_stream_view()->note_height ();
3840 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3842 Drag::start_grab (event);
3844 if (!(_was_selected = _primary->selected())) {
3846 /* tertiary-click means extend selection - we'll do that on button release,
3847 so don't add it here, because otherwise we make it hard to figure
3848 out the "extend-to" range.
3851 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3854 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3857 _region->note_selected (_primary, true);
3859 _region->unique_select (_primary);
3865 /** @return Current total drag x change in frames */
3867 NoteDrag::total_dx () const
3870 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3872 /* primary note time */
3873 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3875 /* new time of the primary note relative to the region position */
3876 frameoffset_t const st = n + dx;
3878 /* snap and return corresponding delta */
3879 return _region->snap_frame_to_frame (st) - n;
3882 /** @return Current total drag y change in notes */
3884 NoteDrag::total_dy () const
3886 /* this is `backwards' to make increasing note number go in the right direction */
3887 double const dy = _drags->current_pointer_y() - grab_y();
3892 if (abs (dy) >= _note_height) {
3894 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3896 ndy = (int8_t) floor (dy / _note_height / 2.0);
3905 NoteDrag::motion (GdkEvent *, bool)
3907 /* Total change in x and y since the start of the drag */
3908 frameoffset_t const dx = total_dx ();
3909 int8_t const dy = total_dy ();
3911 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3912 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3913 double const tdy = dy * _note_height - _cumulative_dy;
3916 _region->move_selection (tdx, tdy);
3917 _cumulative_dx += tdx;
3918 _cumulative_dy += tdy;
3921 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3922 (int) floor (_primary->note()->note() + dy));
3924 _editor->show_verbose_canvas_cursor_with (buf);
3929 NoteDrag::finished (GdkEvent* ev, bool moved)
3932 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3934 if (_was_selected) {
3935 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3937 _region->note_deselected (_primary);
3940 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3941 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3943 if (!extend && !add && _region->selection_size() > 1) {
3944 _region->unique_select (_primary);
3945 } else if (extend) {
3946 _region->note_selected (_primary, true, true);
3948 /* it was added during button press */
3953 _region->note_dropped (_primary, total_dx(), - total_dy());
3958 NoteDrag::aborted ()
3963 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3966 , _nothing_to_drag (false)
3968 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3971 _line = _atav->line ();
3975 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3977 Drag::start_grab (event, cursor);
3979 list<ControlPoint*> points;
3981 XMLNode* state = &_line->get_state ();
3983 if (_ranges.empty()) {
3985 uint32_t const N = _line->npoints ();
3986 for (uint32_t i = 0; i < N; ++i) {
3987 points.push_back (_line->nth (i));
3992 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3993 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3995 /* fade into and out of the region that we're dragging;
3996 64 samples length plucked out of thin air.
3998 nframes64_t const h = (j->start + j->end) / 2;
3999 nframes64_t a = j->start + 64;
4003 nframes64_t b = j->end - 64;
4008 the_list->add (j->start, the_list->eval (j->start));
4009 _line->add_always_in_view (j->start);
4010 the_list->add (a, the_list->eval (a));
4011 _line->add_always_in_view (a);
4012 the_list->add (b, the_list->eval (b));
4013 _line->add_always_in_view (b);
4014 the_list->add (j->end, the_list->eval (j->end));
4015 _line->add_always_in_view (j->end);
4018 uint32_t const N = _line->npoints ();
4019 for (uint32_t i = 0; i < N; ++i) {
4021 ControlPoint* p = _line->nth (i);
4023 list<AudioRange>::const_iterator j = _ranges.begin ();
4024 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
4028 if (j != _ranges.end()) {
4029 points.push_back (p);
4034 if (points.empty()) {
4035 _nothing_to_drag = true;
4039 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
4043 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
4045 if (_nothing_to_drag) {
4049 float const f = 1 - (_drags->current_pointer_y() / _line->height());
4051 /* we are ignoring x position for this drag, so we can just pass in anything */
4052 _line->drag_motion (0, f, true, false);
4056 AutomationRangeDrag::finished (GdkEvent* event, bool)
4058 if (_nothing_to_drag) {
4062 motion (event, false);
4064 _line->clear_always_in_view ();
4068 AutomationRangeDrag::aborted ()
4070 _line->clear_always_in_view ();
4074 DraggingView::DraggingView (RegionView* v)
4077 initial_y = v->get_canvas_group()->property_y ();
4078 initial_playlist = v->region()->playlist ();