2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
26 #include "pbd/memento_command.h"
27 #include "pbd/basename.h"
28 #include "pbd/stateful_diff_command.h"
30 #include "gtkmm2ext/utils.h"
32 #include "ardour/session.h"
33 #include "ardour/dB.h"
34 #include "ardour/region_factory.h"
35 #include "ardour/operations.h"
40 #include "audio_region_view.h"
41 #include "midi_region_view.h"
42 #include "ardour_ui.h"
43 #include "gui_thread.h"
44 #include "control_point.h"
46 #include "region_gain_line.h"
47 #include "editor_drag.h"
48 #include "audio_time_axis.h"
49 #include "midi_time_axis.h"
50 #include "canvas-note.h"
51 #include "selection.h"
52 #include "midi_selection.h"
53 #include "automation_time_axis.h"
55 #include "editor_cursors.h"
56 #include "mouse_cursors.h"
59 using namespace ARDOUR;
62 using namespace Gtkmm2ext;
63 using namespace Editing;
64 using namespace ArdourCanvas;
66 using Gtkmm2ext::Keyboard;
68 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
70 DragManager::DragManager (Editor* e)
73 , _current_pointer_frame (0)
78 DragManager::~DragManager ()
83 /** Call abort for each active drag */
89 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
96 _editor->set_follow_playhead (_old_follow_playhead, false);
102 DragManager::add (Drag* d)
104 d->set_manager (this);
105 _drags.push_back (d);
109 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
111 d->set_manager (this);
112 _drags.push_back (d);
117 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
119 /* Prevent follow playhead during the drag to be nice to the user */
120 _old_follow_playhead = _editor->follow_playhead ();
121 _editor->set_follow_playhead (false);
123 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
125 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
126 (*i)->start_grab (e, c);
130 /** Call end_grab for each active drag.
131 * @return true if any drag reported movement having occurred.
134 DragManager::end_grab (GdkEvent* e)
139 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
140 bool const t = (*i)->end_grab (e);
151 _editor->set_follow_playhead (_old_follow_playhead, false);
157 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
161 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
163 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
164 bool const t = (*i)->motion_handler (e, from_autoscroll);
175 DragManager::have_item (ArdourCanvas::Item* i) const
177 list<Drag*>::const_iterator j = _drags.begin ();
178 while (j != _drags.end() && (*j)->item () != i) {
182 return j != _drags.end ();
185 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
188 , _pointer_frame_offset (0)
189 , _move_threshold_passed (false)
190 , _raw_grab_frame (0)
192 , _last_pointer_frame (0)
198 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
204 cursor = _editor->which_grabber_cursor ();
207 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
211 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
214 cursor = _editor->which_grabber_cursor ();
217 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
219 if (Keyboard::is_button2_event (&event->button)) {
220 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
221 _y_constrained = true;
222 _x_constrained = false;
224 _y_constrained = false;
225 _x_constrained = true;
228 _x_constrained = false;
229 _y_constrained = false;
232 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
233 setup_pointer_frame_offset ();
234 _grab_frame = adjusted_frame (_raw_grab_frame, event);
235 _last_pointer_frame = _grab_frame;
236 _last_pointer_x = _grab_x;
237 _last_pointer_y = _grab_y;
239 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
243 if (_editor->session() && _editor->session()->transport_rolling()) {
246 _was_rolling = false;
249 switch (_editor->snap_type()) {
250 case SnapToRegionStart:
251 case SnapToRegionEnd:
252 case SnapToRegionSync:
253 case SnapToRegionBoundary:
254 _editor->build_region_boundary_cache ();
261 /** Call to end a drag `successfully'. Ungrabs item and calls
262 * subclass' finished() method.
264 * @param event GDK event, or 0.
265 * @return true if some movement occurred, otherwise false.
268 Drag::end_grab (GdkEvent* event)
270 _editor->stop_canvas_autoscroll ();
272 _item->ungrab (event ? event->button.time : 0);
274 finished (event, _move_threshold_passed);
276 _editor->hide_verbose_canvas_cursor();
278 return _move_threshold_passed;
282 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
286 if (f > _pointer_frame_offset) {
287 pos = f - _pointer_frame_offset;
291 _editor->snap_to_with_modifier (pos, event);
298 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
300 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
304 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
306 /* check to see if we have moved in any way that matters since the last motion event */
307 if (_move_threshold_passed &&
308 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
309 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
313 pair<framecnt_t, int> const threshold = move_threshold ();
315 bool const old_move_threshold_passed = _move_threshold_passed;
317 if (!from_autoscroll && !_move_threshold_passed) {
319 bool const xp = (::llabs (_drags->current_pointer_frame () - _grab_frame) >= threshold.first);
320 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
322 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
325 if (active (_editor->mouse_mode) && _move_threshold_passed) {
327 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
328 if (!from_autoscroll) {
329 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
332 motion (event, _move_threshold_passed != old_move_threshold_passed);
334 _last_pointer_x = _drags->current_pointer_x ();
335 _last_pointer_y = _drags->current_pointer_y ();
336 _last_pointer_frame = adjusted_current_frame (event);
344 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
352 aborted (_move_threshold_passed);
354 _editor->stop_canvas_autoscroll ();
355 _editor->hide_verbose_canvas_cursor ();
358 struct EditorOrderTimeAxisViewSorter {
359 bool operator() (TimeAxisView* a, TimeAxisView* b) {
360 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
361 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
363 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
367 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
371 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
373 /* Make a list of non-hidden tracks to refer to during the drag */
375 TrackViewList track_views = _editor->track_views;
376 track_views.sort (EditorOrderTimeAxisViewSorter ());
378 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
379 if (!(*i)->hidden()) {
381 _time_axis_views.push_back (*i);
383 TimeAxisView::Children children_list = (*i)->get_child_list ();
384 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
385 _time_axis_views.push_back (j->get());
390 /* the list of views can be empty at this point if this is a region list-insert drag
393 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
394 _views.push_back (DraggingView (*i, this));
397 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
401 RegionDrag::region_going_away (RegionView* v)
403 list<DraggingView>::iterator i = _views.begin ();
404 while (i != _views.end() && i->view != v) {
408 if (i != _views.end()) {
413 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
415 RegionDrag::find_time_axis_view (TimeAxisView* t) const
418 int const N = _time_axis_views.size ();
419 while (i < N && _time_axis_views[i] != t) {
430 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
431 : RegionDrag (e, i, p, v),
440 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
442 Drag::start_grab (event, cursor);
444 _editor->show_verbose_time_cursor (_last_frame_position, 10);
446 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
447 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
448 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
452 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
454 /* compute the amount of pointer motion in frames, and where
455 the region would be if we moved it by that much.
457 *pending_region_position = adjusted_current_frame (event);
459 framepos_t sync_frame;
460 framecnt_t sync_offset;
463 sync_offset = _primary->region()->sync_offset (sync_dir);
465 /* we don't handle a sync point that lies before zero.
467 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
469 sync_frame = *pending_region_position + (sync_dir*sync_offset);
471 _editor->snap_to_with_modifier (sync_frame, event);
473 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
476 *pending_region_position = _last_frame_position;
479 if (*pending_region_position > max_framepos - _primary->region()->length()) {
480 *pending_region_position = _last_frame_position;
485 /* in locked edit mode, reverse the usual meaning of _x_constrained */
486 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
488 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
490 /* x movement since last time */
491 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
493 /* total x movement */
494 framecnt_t total_dx = *pending_region_position;
495 if (regions_came_from_canvas()) {
496 total_dx = total_dx - grab_frame () + _pointer_frame_offset;
499 /* check that no regions have gone off the start of the session */
500 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
501 if ((i->view->region()->position() + total_dx) < 0) {
503 *pending_region_position = _last_frame_position;
508 _last_frame_position = *pending_region_position;
515 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
517 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
518 int const n = i->time_axis_view + delta_track;
519 if (n < 0 || n >= int (_time_axis_views.size())) {
520 /* off the top or bottom track */
524 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
525 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
526 /* not a track, or the wrong type */
530 int const l = i->layer + delta_layer;
531 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
532 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
533 If it has, the layers will be munged later anyway, so it's ok.
539 /* all regions being dragged are ok with this change */
544 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
546 assert (!_views.empty ());
548 /* Find the TimeAxisView that the pointer is now over */
549 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
551 /* Bail early if we're not over a track */
552 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
553 if (!rtv || !rtv->is_track()) {
554 _editor->hide_verbose_canvas_cursor ();
558 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
560 /* Here's the current pointer position in terms of time axis view and layer */
561 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
562 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
564 /* Work out the change in x */
565 framepos_t pending_region_position;
566 double const x_delta = compute_x_delta (event, &pending_region_position);
568 /* Work out the change in y */
569 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
570 int delta_layer = current_pointer_layer - _last_pointer_layer;
572 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
573 /* this y movement is not allowed, so do no y movement this time */
574 delta_time_axis_view = 0;
578 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
579 /* haven't reached next snap point, and we're not switching
580 trackviews nor layers. nothing to do.
585 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
587 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
589 RegionView* rv = i->view;
591 if (rv->region()->locked()) {
597 /* here we are calculating the y distance from the
598 top of the first track view to the top of the region
599 area of the track view that we're working on */
601 /* this x value is just a dummy value so that we have something
606 /* distance from the top of this track view to the region area
607 of our track view is always 1 */
611 /* convert to world coordinates, ie distance from the top of
614 rv->get_canvas_frame()->i2w (ix1, iy1);
616 /* compensate for the ruler section and the vertical scrollbar position */
617 iy1 += _editor->get_trackview_group_vertical_offset ();
619 // hide any dependent views
621 rv->get_time_axis_view().hide_dependent_views (*rv);
624 reparent to a non scrolling group so that we can keep the
625 region selection above all time axis views.
626 reparenting means we have to move the rv as the two
627 parent groups have different coordinates.
630 rv->get_canvas_group()->property_y() = iy1 - 1;
631 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
633 rv->fake_set_opaque (true);
636 /* Work out the change in y position of this region view */
640 /* If we have moved tracks, we'll fudge the layer delta so that the
641 region gets moved back onto layer 0 on its new track; this avoids
642 confusion when dragging regions from non-zero layers onto different
645 int this_delta_layer = delta_layer;
646 if (delta_time_axis_view != 0) {
647 this_delta_layer = - i->layer;
650 /* Move this region to layer 0 on its old track */
651 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
652 if (lv->layer_display() == Stacked) {
653 y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
656 /* Now move it to its right layer on the current track */
657 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
658 if (cv->layer_display() == Stacked) {
659 y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
663 if (delta_time_axis_view > 0) {
664 for (int j = 0; j < delta_time_axis_view; ++j) {
665 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
668 /* start by subtracting the height of the track above where we are now */
669 for (int j = 1; j <= -delta_time_axis_view; ++j) {
670 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
675 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
677 /* Update the DraggingView */
678 i->time_axis_view += delta_time_axis_view;
679 i->layer += this_delta_layer;
682 _editor->mouse_brush_insert_region (rv, pending_region_position);
684 rv->move (x_delta, y_delta);
687 } /* foreach region */
689 _total_x_delta += x_delta;
692 _editor->cursor_group->raise_to_top();
695 if (x_delta != 0 && !_brushing) {
696 _editor->show_verbose_time_cursor (_last_frame_position, 10);
699 _last_pointer_time_axis_view += delta_time_axis_view;
700 _last_pointer_layer += delta_layer;
704 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
706 if (_copy && first_move) {
708 /* duplicate the regionview(s) and region(s) */
710 list<DraggingView> new_regionviews;
712 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
714 RegionView* rv = i->view;
715 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
716 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
718 const boost::shared_ptr<const Region> original = rv->region();
719 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
720 region_copy->set_position (original->position(), this);
724 boost::shared_ptr<AudioRegion> audioregion_copy
725 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
727 nrv = new AudioRegionView (*arv, audioregion_copy);
729 boost::shared_ptr<MidiRegion> midiregion_copy
730 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
731 nrv = new MidiRegionView (*mrv, midiregion_copy);
736 nrv->get_canvas_group()->show ();
737 new_regionviews.push_back (DraggingView (nrv, this));
739 /* swap _primary to the copy */
741 if (rv == _primary) {
745 /* ..and deselect the one we copied */
747 rv->set_selected (false);
750 if (!new_regionviews.empty()) {
752 /* reflect the fact that we are dragging the copies */
754 _views = new_regionviews;
756 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
759 sync the canvas to what we think is its current state
760 without it, the canvas seems to
761 "forget" to update properly after the upcoming reparent()
762 ..only if the mouse is in rapid motion at the time of the grab.
763 something to do with regionview creation taking so long?
765 _editor->update_canvas_now();
769 RegionMotionDrag::motion (event, first_move);
773 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
775 if (!movement_occurred) {
780 /* reverse this here so that we have the correct logic to finalize
784 if (Config->get_edit_mode() == Lock) {
785 _x_constrained = !_x_constrained;
788 assert (!_views.empty ());
790 bool const changed_position = (_last_frame_position != _primary->region()->position());
791 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
792 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
794 _editor->update_canvas_now ();
816 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
818 RegionSelection new_views;
819 PlaylistSet modified_playlists;
820 list<RegionView*> views_to_delete;
823 /* all changes were made during motion event handlers */
825 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
829 _editor->commit_reversible_command ();
833 if (_x_constrained) {
834 _editor->begin_reversible_command (_("fixed time region copy"));
836 _editor->begin_reversible_command (_("region copy"));
839 /* insert the regions into their new playlists */
840 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
842 if (i->view->region()->locked()) {
848 if (changed_position && !_x_constrained) {
849 where = i->view->region()->position() - drag_delta;
851 where = i->view->region()->position();
854 RegionView* new_view = insert_region_into_playlist (
855 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
862 new_views.push_back (new_view);
864 /* we don't need the copied RegionView any more */
865 views_to_delete.push_back (i->view);
868 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
869 because when views are deleted they are automagically removed from _views, which messes
872 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
876 /* If we've created new regions either by copying or moving
877 to a new track, we want to replace the old selection with the new ones
880 if (new_views.size() > 0) {
881 _editor->selection->set (new_views);
884 /* write commands for the accumulated diffs for all our modified playlists */
885 add_stateful_diff_commands_for_playlists (modified_playlists);
887 _editor->commit_reversible_command ();
891 RegionMoveDrag::finished_no_copy (
892 bool const changed_position,
893 bool const changed_tracks,
894 framecnt_t const drag_delta
897 RegionSelection new_views;
898 PlaylistSet modified_playlists;
899 PlaylistSet frozen_playlists;
902 /* all changes were made during motion event handlers */
903 _editor->commit_reversible_command ();
907 if (_x_constrained) {
908 _editor->begin_reversible_command (_("fixed time region drag"));
910 _editor->begin_reversible_command (Operations::region_drag);
913 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
915 RegionView* rv = i->view;
917 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
918 layer_t const dest_layer = i->layer;
920 if (rv->region()->locked()) {
927 if (changed_position && !_x_constrained) {
928 where = rv->region()->position() - drag_delta;
930 where = rv->region()->position();
933 if (changed_tracks) {
935 /* insert into new playlist */
937 RegionView* new_view = insert_region_into_playlist (
938 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
946 new_views.push_back (new_view);
948 /* remove from old playlist */
950 /* the region that used to be in the old playlist is not
951 moved to the new one - we use a copy of it. as a result,
952 any existing editor for the region should no longer be
955 rv->hide_region_editor();
956 rv->fake_set_opaque (false);
958 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
962 rv->region()->clear_changes ();
965 motion on the same track. plonk the previously reparented region
966 back to its original canvas group (its streamview).
967 No need to do anything for copies as they are fake regions which will be deleted.
970 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
971 rv->get_canvas_group()->property_y() = i->initial_y;
972 rv->get_time_axis_view().reveal_dependent_views (*rv);
974 /* just change the model */
976 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
978 if (dest_rtv->view()->layer_display() == Stacked) {
979 rv->region()->set_layer (dest_layer);
980 rv->region()->set_pending_explicit_relayer (true);
983 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
985 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
991 /* this movement may result in a crossfade being modified, so we need to get undo
992 data from the playlist as well as the region.
995 r = modified_playlists.insert (playlist);
997 playlist->clear_changes ();
1000 rv->region()->set_position (where, (void*) this);
1002 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1005 if (changed_tracks) {
1007 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1008 was selected in all of them, then removing it from a playlist will have removed all
1009 trace of it from _views (i.e. there were N regions selected, we removed 1,
1010 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1011 corresponding regionview, and _views is now empty).
1013 This could have invalidated any and all iterators into _views.
1015 The heuristic we use here is: if the region selection is empty, break out of the loop
1016 here. if the region selection is not empty, then restart the loop because we know that
1017 we must have removed at least the region(view) we've just been working on as well as any
1018 that we processed on previous iterations.
1020 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1021 we can just iterate.
1025 if (_views.empty()) {
1036 /* If we've created new regions either by copying or moving
1037 to a new track, we want to replace the old selection with the new ones
1040 if (new_views.size() > 0) {
1041 _editor->selection->set (new_views);
1044 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1048 /* write commands for the accumulated diffs for all our modified playlists */
1049 add_stateful_diff_commands_for_playlists (modified_playlists);
1051 _editor->commit_reversible_command ();
1054 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1055 * @param region Region to remove.
1056 * @param playlist playlist To remove from.
1057 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1058 * that clear_changes () is only called once per playlist.
1061 RegionMoveDrag::remove_region_from_playlist (
1062 boost::shared_ptr<Region> region,
1063 boost::shared_ptr<Playlist> playlist,
1064 PlaylistSet& modified_playlists
1067 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1070 playlist->clear_changes ();
1073 playlist->remove_region (region);
1077 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1078 * clearing the playlist's diff history first if necessary.
1079 * @param region Region to insert.
1080 * @param dest_rtv Destination RouteTimeAxisView.
1081 * @param dest_layer Destination layer.
1082 * @param where Destination position.
1083 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1084 * that clear_changes () is only called once per playlist.
1085 * @return New RegionView, or 0 if no insert was performed.
1088 RegionMoveDrag::insert_region_into_playlist (
1089 boost::shared_ptr<Region> region,
1090 RouteTimeAxisView* dest_rtv,
1093 PlaylistSet& modified_playlists
1096 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1097 if (!dest_playlist) {
1101 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1102 _new_region_view = 0;
1103 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1105 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1106 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1108 dest_playlist->clear_changes ();
1111 dest_playlist->add_region (region, where);
1113 if (dest_rtv->view()->layer_display() == Stacked) {
1114 region->set_layer (dest_layer);
1115 region->set_pending_explicit_relayer (true);
1120 assert (_new_region_view);
1122 return _new_region_view;
1126 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1128 _new_region_view = rv;
1132 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1134 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1135 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1137 _editor->session()->add_command (c);
1146 RegionMoveDrag::aborted (bool movement_occurred)
1150 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1157 RegionMotionDrag::aborted (movement_occurred);
1162 RegionMotionDrag::aborted (bool)
1164 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1165 RegionView* rv = i->view;
1166 TimeAxisView* tv = &(rv->get_time_axis_view ());
1167 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1169 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1170 rv->get_canvas_group()->property_y() = 0;
1171 rv->get_time_axis_view().reveal_dependent_views (*rv);
1172 rv->fake_set_opaque (false);
1173 rv->move (-_total_x_delta, 0);
1174 rv->set_height (rtv->view()->child_height ());
1177 _editor->update_canvas_now ();
1180 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1181 : RegionMotionDrag (e, i, p, v, b),
1184 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1187 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1188 if (rtv && rtv->is_track()) {
1189 speed = rtv->track()->speed ();
1192 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1196 RegionMoveDrag::setup_pointer_frame_offset ()
1198 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1201 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1202 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1204 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1206 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1207 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1209 _primary = v->view()->create_region_view (r, false, false);
1211 _primary->get_canvas_group()->show ();
1212 _primary->set_position (pos, 0);
1213 _views.push_back (DraggingView (_primary, this));
1215 _last_frame_position = pos;
1217 _item = _primary->get_canvas_group ();
1221 RegionInsertDrag::finished (GdkEvent *, bool)
1223 _editor->update_canvas_now ();
1225 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1227 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1228 _primary->get_canvas_group()->property_y() = 0;
1230 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1232 _editor->begin_reversible_command (Operations::insert_region);
1233 playlist->clear_changes ();
1234 playlist->add_region (_primary->region (), _last_frame_position);
1235 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1236 _editor->commit_reversible_command ();
1244 RegionInsertDrag::aborted (bool)
1251 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1252 : RegionMoveDrag (e, i, p, v, false, false)
1254 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1257 struct RegionSelectionByPosition {
1258 bool operator() (RegionView*a, RegionView* b) {
1259 return a->region()->position () < b->region()->position();
1264 RegionSpliceDrag::motion (GdkEvent* event, bool)
1266 /* Which trackview is this ? */
1268 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1269 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1270 layer_t layer = tvp.second;
1272 if (tv && tv->layer_display() == Overlaid) {
1276 /* The region motion is only processed if the pointer is over
1280 if (!tv || !tv->is_track()) {
1281 /* To make sure we hide the verbose canvas cursor when the mouse is
1282 not held over and audiotrack.
1284 _editor->hide_verbose_canvas_cursor ();
1290 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1296 RegionSelection copy (_editor->selection->regions);
1298 RegionSelectionByPosition cmp;
1301 framepos_t const pf = adjusted_current_frame (event);
1303 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1305 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1311 boost::shared_ptr<Playlist> playlist;
1313 if ((playlist = atv->playlist()) == 0) {
1317 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1322 if (pf < (*i)->region()->last_frame() + 1) {
1326 if (pf > (*i)->region()->first_frame()) {
1332 playlist->shuffle ((*i)->region(), dir);
1337 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1339 RegionMoveDrag::finished (event, movement_occurred);
1343 RegionSpliceDrag::aborted (bool)
1348 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1350 _view (dynamic_cast<MidiTimeAxisView*> (v))
1352 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1358 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1362 _view->playlist()->freeze ();
1365 framepos_t const f = adjusted_current_frame (event);
1366 if (f < grab_frame()) {
1367 _region->set_position (f, this);
1370 /* again, don't use a zero-length region (see above) */
1371 framecnt_t const len = abs (f - grab_frame ());
1372 _region->set_length (len < 1 ? 1 : len, this);
1378 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1380 if (!movement_occurred) {
1383 _view->playlist()->thaw ();
1387 _editor->commit_reversible_command ();
1392 RegionCreateDrag::add_region ()
1394 if (_editor->session()) {
1395 const TempoMap& map (_editor->session()->tempo_map());
1396 framecnt_t pos = grab_frame();
1397 const Meter& m = map.meter_at (pos);
1398 /* not that the frame rate used here can be affected by pull up/down which
1401 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1402 _region = _view->add_region (grab_frame(), len, false);
1407 RegionCreateDrag::aborted (bool)
1410 _view->playlist()->thaw ();
1416 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1420 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1424 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1426 Gdk::Cursor* cursor;
1427 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1428 float x_fraction = cnote->mouse_x_fraction ();
1430 if (x_fraction > 0.0 && x_fraction < 0.25) {
1431 cursor = _editor->cursors()->left_side_trim;
1433 cursor = _editor->cursors()->right_side_trim;
1436 Drag::start_grab (event, cursor);
1438 region = &cnote->region_view();
1440 double const region_start = region->get_position_pixels();
1441 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1443 if (grab_x() <= middle_point) {
1444 cursor = _editor->cursors()->left_side_trim;
1447 cursor = _editor->cursors()->right_side_trim;
1451 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1453 if (event->motion.state & Keyboard::PrimaryModifier) {
1459 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1461 if (ms.size() > 1) {
1462 /* has to be relative, may make no sense otherwise */
1466 /* select this note; if it is already selected, preserve the existing selection,
1467 otherwise make this note the only one selected.
1469 region->note_selected (cnote, cnote->selected ());
1471 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1472 MidiRegionSelection::iterator next;
1475 (*r)->begin_resizing (at_front);
1481 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1483 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1484 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1485 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1490 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1492 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1493 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1494 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1499 NoteResizeDrag::aborted (bool)
1504 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1507 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1511 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1517 RegionGainDrag::finished (GdkEvent *, bool)
1523 RegionGainDrag::aborted (bool)
1528 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1529 : RegionDrag (e, i, p, v)
1531 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1535 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1538 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1539 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1541 if (tv && tv->is_track()) {
1542 speed = tv->track()->speed();
1545 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1546 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1547 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1549 framepos_t const pf = adjusted_current_frame (event);
1551 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1552 /* Move the contents of the region around without changing the region bounds */
1553 _operation = ContentsTrim;
1554 Drag::start_grab (event, _editor->cursors()->trimmer);
1556 /* These will get overridden for a point trim.*/
1557 if (pf < (region_start + region_length/2)) {
1558 /* closer to front */
1559 _operation = StartTrim;
1560 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1563 _operation = EndTrim;
1564 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1568 switch (_operation) {
1570 _editor->show_verbose_time_cursor (region_start, 10);
1571 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1572 i->view->trim_front_starting ();
1576 _editor->show_verbose_time_cursor (region_end, 10);
1579 _editor->show_verbose_time_cursor (pf, 10);
1583 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1584 i->view->region()->suspend_property_changes ();
1589 TrimDrag::motion (GdkEvent* event, bool first_move)
1591 RegionView* rv = _primary;
1594 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1595 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1596 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1598 if (tv && tv->is_track()) {
1599 speed = tv->track()->speed();
1602 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1608 switch (_operation) {
1610 trim_type = "Region start trim";
1613 trim_type = "Region end trim";
1616 trim_type = "Region content trim";
1620 _editor->begin_reversible_command (trim_type);
1622 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1623 RegionView* rv = i->view;
1624 rv->fake_set_opaque (false);
1625 rv->enable_display (false);
1626 rv->region()->playlist()->clear_owned_changes ();
1628 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1631 arv->temporarily_hide_envelope ();
1634 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1635 insert_result = _editor->motion_frozen_playlists.insert (pl);
1637 if (insert_result.second) {
1643 bool non_overlap_trim = false;
1645 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1646 non_overlap_trim = true;
1649 switch (_operation) {
1651 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1652 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1657 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1658 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1664 bool swap_direction = false;
1666 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1667 swap_direction = true;
1670 framecnt_t frame_delta = 0;
1672 bool left_direction = false;
1673 if (last_pointer_frame() > adjusted_current_frame(event)) {
1674 left_direction = true;
1677 if (left_direction) {
1678 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1680 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1683 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1684 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1690 switch (_operation) {
1692 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1695 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1698 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1705 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1707 if (movement_occurred) {
1708 motion (event, false);
1710 /* This must happen before the region's StatefulDiffCommand is created, as it may
1711 `correct' (ahem) the region's _start from being negative to being zero. It
1712 needs to be zero in the undo record.
1714 if (_operation == StartTrim) {
1715 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1716 i->view->trim_front_ending ();
1720 if (!_editor->selection->selected (_primary)) {
1721 _primary->thaw_after_trim ();
1724 set<boost::shared_ptr<Playlist> > diffed_playlists;
1726 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1727 i->view->thaw_after_trim ();
1728 i->view->enable_display (true);
1729 i->view->fake_set_opaque (true);
1731 /* Trimming one region may affect others on the playlist, so we need
1732 to get undo Commands from the whole playlist rather than just the
1733 region. Use diffed_playlists to make sure we don't diff a given
1734 playlist more than once.
1736 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1737 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1738 vector<Command*> cmds;
1740 _editor->session()->add_commands (cmds);
1741 diffed_playlists.insert (p);
1745 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1749 _editor->motion_frozen_playlists.clear ();
1750 _editor->commit_reversible_command();
1753 /* no mouse movement */
1754 _editor->point_trim (event, adjusted_current_frame (event));
1757 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1758 if (_operation == StartTrim) {
1759 i->view->trim_front_ending ();
1762 i->view->region()->resume_property_changes ();
1767 TrimDrag::aborted (bool movement_occurred)
1769 /* Our motion method is changing model state, so use the Undo system
1770 to cancel. Perhaps not ideal, as this will leave an Undo point
1771 behind which may be slightly odd from the user's point of view.
1776 if (movement_occurred) {
1780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1781 i->view->region()->resume_property_changes ();
1786 TrimDrag::setup_pointer_frame_offset ()
1788 list<DraggingView>::iterator i = _views.begin ();
1789 while (i != _views.end() && i->view != _primary) {
1793 if (i == _views.end()) {
1797 switch (_operation) {
1799 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1802 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1809 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1813 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1815 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1820 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1823 // create a dummy marker for visual representation of moving the copy.
1824 // The actual copying is not done before we reach the finish callback.
1826 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1828 MeterMarker* new_marker = new MeterMarker (
1830 *_editor->meter_group,
1831 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1833 *new MeterSection (_marker->meter())
1836 _item = &new_marker->the_item ();
1837 _marker = new_marker;
1841 MetricSection& section (_marker->meter());
1843 if (!section.movable()) {
1849 Drag::start_grab (event, cursor);
1851 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1855 MeterMarkerDrag::setup_pointer_frame_offset ()
1857 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1861 MeterMarkerDrag::motion (GdkEvent* event, bool)
1863 framepos_t const pf = adjusted_current_frame (event);
1865 _marker->set_position (pf);
1867 _editor->show_verbose_time_cursor (pf, 10);
1871 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1873 if (!movement_occurred) {
1877 motion (event, false);
1879 Timecode::BBT_Time when;
1881 TempoMap& map (_editor->session()->tempo_map());
1882 map.bbt_time (last_pointer_frame(), when);
1884 if (_copy == true) {
1885 _editor->begin_reversible_command (_("copy meter mark"));
1886 XMLNode &before = map.get_state();
1887 map.add_meter (_marker->meter(), when);
1888 XMLNode &after = map.get_state();
1889 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1890 _editor->commit_reversible_command ();
1892 // delete the dummy marker we used for visual representation of copying.
1893 // a new visual marker will show up automatically.
1896 _editor->begin_reversible_command (_("move meter mark"));
1897 XMLNode &before = map.get_state();
1898 map.move_meter (_marker->meter(), when);
1899 XMLNode &after = map.get_state();
1900 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1901 _editor->commit_reversible_command ();
1906 MeterMarkerDrag::aborted (bool)
1908 _marker->set_position (_marker->meter().frame ());
1911 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1915 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1917 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1922 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1926 // create a dummy marker for visual representation of moving the copy.
1927 // The actual copying is not done before we reach the finish callback.
1929 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1931 TempoMarker* new_marker = new TempoMarker (
1933 *_editor->tempo_group,
1934 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1936 *new TempoSection (_marker->tempo())
1939 _item = &new_marker->the_item ();
1940 _marker = new_marker;
1944 Drag::start_grab (event, cursor);
1946 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1950 TempoMarkerDrag::setup_pointer_frame_offset ()
1952 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1956 TempoMarkerDrag::motion (GdkEvent* event, bool)
1958 framepos_t const pf = adjusted_current_frame (event);
1959 _marker->set_position (pf);
1960 _editor->show_verbose_time_cursor (pf, 10);
1964 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1966 if (!movement_occurred) {
1970 motion (event, false);
1972 Timecode::BBT_Time when;
1974 TempoMap& map (_editor->session()->tempo_map());
1975 map.bbt_time (last_pointer_frame(), when);
1977 if (_copy == true) {
1978 _editor->begin_reversible_command (_("copy tempo mark"));
1979 XMLNode &before = map.get_state();
1980 map.add_tempo (_marker->tempo(), when);
1981 XMLNode &after = map.get_state();
1982 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1983 _editor->commit_reversible_command ();
1985 // delete the dummy marker we used for visual representation of copying.
1986 // a new visual marker will show up automatically.
1989 _editor->begin_reversible_command (_("move tempo mark"));
1990 XMLNode &before = map.get_state();
1991 map.move_tempo (_marker->tempo(), when);
1992 XMLNode &after = map.get_state();
1993 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1994 _editor->commit_reversible_command ();
1999 TempoMarkerDrag::aborted (bool)
2001 _marker->set_position (_marker->tempo().frame());
2004 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2008 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2011 /** Do all the things we do when dragging the playhead to make it look as though
2012 * we have located, without actually doing the locate (because that would cause
2013 * the diskstream buffers to be refilled, which is too slow).
2016 CursorDrag::fake_locate (framepos_t t)
2018 _editor->playhead_cursor->set_position (t);
2020 Session* s = _editor->session ();
2021 if (s->timecode_transmission_suspended ()) {
2022 framepos_t const f = _editor->playhead_cursor->current_frame;
2023 s->send_mmc_locate (f);
2024 s->send_full_time_code (f);
2027 _editor->show_verbose_time_cursor (t, 10);
2028 _editor->UpdateAllTransportClocks (t);
2032 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2034 Drag::start_grab (event, c);
2036 framepos_t where = _editor->event_frame (event, 0, 0);
2037 _editor->snap_to_with_modifier (where, event);
2039 _editor->_dragging_playhead = true;
2041 Session* s = _editor->session ();
2044 if (_was_rolling && _stop) {
2048 if (s->is_auditioning()) {
2049 s->cancel_audition ();
2052 s->request_suspend_timecode_transmission ();
2053 while (!s->timecode_transmission_suspended ()) {
2054 /* twiddle our thumbs */
2058 fake_locate (where);
2062 CursorDrag::motion (GdkEvent* event, bool)
2064 framepos_t const adjusted_frame = adjusted_current_frame (event);
2066 if (adjusted_frame == last_pointer_frame()) {
2070 fake_locate (adjusted_frame);
2073 _editor->update_canvas_now ();
2078 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2080 _editor->_dragging_playhead = false;
2082 if (!movement_occurred && _stop) {
2086 motion (event, false);
2088 Session* s = _editor->session ();
2090 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2091 _editor->_pending_locate_request = true;
2092 s->request_resume_timecode_transmission ();
2097 CursorDrag::aborted (bool)
2099 if (_editor->_dragging_playhead) {
2100 _editor->session()->request_resume_timecode_transmission ();
2101 _editor->_dragging_playhead = false;
2104 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2107 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2108 : RegionDrag (e, i, p, v)
2110 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2114 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2116 Drag::start_grab (event, cursor);
2118 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2119 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2121 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2123 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2127 FadeInDrag::setup_pointer_frame_offset ()
2129 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2130 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2131 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2135 FadeInDrag::motion (GdkEvent* event, bool)
2137 framecnt_t fade_length;
2139 framepos_t const pos = adjusted_current_frame (event);
2141 boost::shared_ptr<Region> region = _primary->region ();
2143 if (pos < (region->position() + 64)) {
2144 fade_length = 64; // this should be a minimum defined somewhere
2145 } else if (pos > region->last_frame()) {
2146 fade_length = region->length();
2148 fade_length = pos - region->position();
2151 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2153 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2159 tmp->reset_fade_in_shape_width (fade_length);
2160 tmp->show_fade_line((framecnt_t) fade_length);
2163 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2167 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2169 if (!movement_occurred) {
2173 framecnt_t fade_length;
2175 framepos_t const pos = adjusted_current_frame (event);
2177 boost::shared_ptr<Region> region = _primary->region ();
2179 if (pos < (region->position() + 64)) {
2180 fade_length = 64; // this should be a minimum defined somewhere
2181 } else if (pos > region->last_frame()) {
2182 fade_length = region->length();
2184 fade_length = pos - region->position();
2187 _editor->begin_reversible_command (_("change fade in length"));
2189 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2191 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2197 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2198 XMLNode &before = alist->get_state();
2200 tmp->audio_region()->set_fade_in_length (fade_length);
2201 tmp->audio_region()->set_fade_in_active (true);
2202 tmp->hide_fade_line();
2204 XMLNode &after = alist->get_state();
2205 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2208 _editor->commit_reversible_command ();
2212 FadeInDrag::aborted (bool)
2214 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2215 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2221 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2222 tmp->hide_fade_line();
2226 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2227 : RegionDrag (e, i, p, v)
2229 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2233 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2235 Drag::start_grab (event, cursor);
2237 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2238 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2240 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2242 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2246 FadeOutDrag::setup_pointer_frame_offset ()
2248 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2249 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2250 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2254 FadeOutDrag::motion (GdkEvent* event, bool)
2256 framecnt_t fade_length;
2258 framepos_t const pos = adjusted_current_frame (event);
2260 boost::shared_ptr<Region> region = _primary->region ();
2262 if (pos > (region->last_frame() - 64)) {
2263 fade_length = 64; // this should really be a minimum fade defined somewhere
2265 else if (pos < region->position()) {
2266 fade_length = region->length();
2269 fade_length = region->last_frame() - pos;
2272 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2274 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2280 tmp->reset_fade_out_shape_width (fade_length);
2281 tmp->show_fade_line(region->length() - fade_length);
2284 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2288 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2290 if (!movement_occurred) {
2294 framecnt_t fade_length;
2296 framepos_t const pos = adjusted_current_frame (event);
2298 boost::shared_ptr<Region> region = _primary->region ();
2300 if (pos > (region->last_frame() - 64)) {
2301 fade_length = 64; // this should really be a minimum fade defined somewhere
2303 else if (pos < region->position()) {
2304 fade_length = region->length();
2307 fade_length = region->last_frame() - pos;
2310 _editor->begin_reversible_command (_("change fade out length"));
2312 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2314 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2320 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2321 XMLNode &before = alist->get_state();
2323 tmp->audio_region()->set_fade_out_length (fade_length);
2324 tmp->audio_region()->set_fade_out_active (true);
2325 tmp->hide_fade_line();
2327 XMLNode &after = alist->get_state();
2328 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2331 _editor->commit_reversible_command ();
2335 FadeOutDrag::aborted (bool)
2337 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2338 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2344 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2345 tmp->hide_fade_line();
2349 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2352 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2354 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2357 _points.push_back (Gnome::Art::Point (0, 0));
2358 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2361 MarkerDrag::~MarkerDrag ()
2363 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2369 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2371 Drag::start_grab (event, cursor);
2375 Location *location = _editor->find_location_from_marker (_marker, is_start);
2376 _editor->_dragging_edit_point = true;
2378 update_item (location);
2380 // _drag_line->show();
2381 // _line->raise_to_top();
2384 _editor->show_verbose_time_cursor (location->start(), 10);
2386 _editor->show_verbose_time_cursor (location->end(), 10);
2389 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2392 case Selection::Toggle:
2393 _editor->selection->toggle (_marker);
2395 case Selection::Set:
2396 if (!_editor->selection->selected (_marker)) {
2397 _editor->selection->set (_marker);
2400 case Selection::Extend:
2402 Locations::LocationList ll;
2403 list<Marker*> to_add;
2405 _editor->selection->markers.range (s, e);
2406 s = min (_marker->position(), s);
2407 e = max (_marker->position(), e);
2410 if (e < max_framepos) {
2413 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2414 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2415 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2418 to_add.push_back (lm->start);
2421 to_add.push_back (lm->end);
2425 if (!to_add.empty()) {
2426 _editor->selection->add (to_add);
2430 case Selection::Add:
2431 _editor->selection->add (_marker);
2435 /* Set up copies for us to manipulate during the drag */
2437 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2438 Location* l = _editor->find_location_from_marker (*i, is_start);
2439 _copied_locations.push_back (new Location (*l));
2444 MarkerDrag::setup_pointer_frame_offset ()
2447 Location *location = _editor->find_location_from_marker (_marker, is_start);
2448 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2452 MarkerDrag::motion (GdkEvent* event, bool)
2454 framecnt_t f_delta = 0;
2456 bool move_both = false;
2458 Location *real_location;
2459 Location *copy_location = 0;
2461 framepos_t const newframe = adjusted_current_frame (event);
2463 framepos_t next = newframe;
2465 if (newframe == last_pointer_frame()) {
2469 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2473 MarkerSelection::iterator i;
2474 list<Location*>::iterator x;
2476 /* find the marker we're dragging, and compute the delta */
2478 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2479 x != _copied_locations.end() && i != _editor->selection->markers.end();
2485 if (marker == _marker) {
2487 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2492 if (real_location->is_mark()) {
2493 f_delta = newframe - copy_location->start();
2497 switch (marker->type()) {
2498 case Marker::SessionStart:
2499 case Marker::RangeStart:
2500 case Marker::LoopStart:
2501 case Marker::PunchIn:
2502 f_delta = newframe - copy_location->start();
2505 case Marker::SessionEnd:
2506 case Marker::RangeEnd:
2507 case Marker::LoopEnd:
2508 case Marker::PunchOut:
2509 f_delta = newframe - copy_location->end();
2512 /* what kind of marker is this ? */
2520 if (i == _editor->selection->markers.end()) {
2521 /* hmm, impossible - we didn't find the dragged marker */
2525 /* now move them all */
2527 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2528 x != _copied_locations.end() && i != _editor->selection->markers.end();
2534 /* call this to find out if its the start or end */
2536 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2540 if (real_location->locked()) {
2544 if (copy_location->is_mark()) {
2548 copy_location->set_start (copy_location->start() + f_delta);
2552 framepos_t new_start = copy_location->start() + f_delta;
2553 framepos_t new_end = copy_location->end() + f_delta;
2555 if (is_start) { // start-of-range marker
2558 copy_location->set_start (new_start);
2559 copy_location->set_end (new_end);
2560 } else if (new_start < copy_location->end()) {
2561 copy_location->set_start (new_start);
2562 } else if (newframe > 0) {
2563 _editor->snap_to (next, 1, true);
2564 copy_location->set_end (next);
2565 copy_location->set_start (newframe);
2568 } else { // end marker
2571 copy_location->set_end (new_end);
2572 copy_location->set_start (new_start);
2573 } else if (new_end > copy_location->start()) {
2574 copy_location->set_end (new_end);
2575 } else if (newframe > 0) {
2576 _editor->snap_to (next, -1, true);
2577 copy_location->set_start (next);
2578 copy_location->set_end (newframe);
2583 update_item (copy_location);
2585 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2588 lm->set_position (copy_location->start(), copy_location->end());
2592 assert (!_copied_locations.empty());
2594 _editor->show_verbose_time_cursor (newframe, 10);
2597 _editor->update_canvas_now ();
2602 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2604 if (!movement_occurred) {
2606 /* just a click, do nothing but finish
2607 off the selection process
2610 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2613 case Selection::Set:
2614 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2615 _editor->selection->set (_marker);
2619 case Selection::Toggle:
2620 case Selection::Extend:
2621 case Selection::Add:
2628 _editor->_dragging_edit_point = false;
2630 _editor->begin_reversible_command ( _("move marker") );
2631 XMLNode &before = _editor->session()->locations()->get_state();
2633 MarkerSelection::iterator i;
2634 list<Location*>::iterator x;
2637 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2638 x != _copied_locations.end() && i != _editor->selection->markers.end();
2641 Location * location = _editor->find_location_from_marker (*i, is_start);
2645 if (location->locked()) {
2649 if (location->is_mark()) {
2650 location->set_start ((*x)->start());
2652 location->set ((*x)->start(), (*x)->end());
2657 XMLNode &after = _editor->session()->locations()->get_state();
2658 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2659 _editor->commit_reversible_command ();
2663 MarkerDrag::aborted (bool)
2669 MarkerDrag::update_item (Location* location)
2674 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2676 _cumulative_x_drag (0),
2677 _cumulative_y_drag (0)
2679 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2681 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2687 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2689 Drag::start_grab (event, _editor->cursors()->fader);
2691 // start the grab at the center of the control point so
2692 // the point doesn't 'jump' to the mouse after the first drag
2693 _fixed_grab_x = _point->get_x();
2694 _fixed_grab_y = _point->get_y();
2696 float const fraction = 1 - (_point->get_y() / _point->line().height());
2698 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2700 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2701 event->button.x + 10, event->button.y + 10);
2703 _editor->show_verbose_canvas_cursor ();
2707 ControlPointDrag::motion (GdkEvent* event, bool)
2709 double dx = _drags->current_pointer_x() - last_pointer_x();
2710 double dy = _drags->current_pointer_y() - last_pointer_y();
2712 if (event->button.state & Keyboard::SecondaryModifier) {
2717 /* coordinate in pixels relative to the start of the region (for region-based automation)
2718 or track (for track-based automation) */
2719 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2720 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2722 // calculate zero crossing point. back off by .01 to stay on the
2723 // positive side of zero
2724 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2726 // make sure we hit zero when passing through
2727 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2731 if (_x_constrained) {
2734 if (_y_constrained) {
2738 _cumulative_x_drag = cx - _fixed_grab_x;
2739 _cumulative_y_drag = cy - _fixed_grab_y;
2743 cy = min ((double) _point->line().height(), cy);
2745 framepos_t cx_frames = _editor->unit_to_frame (cx);
2747 if (!_x_constrained) {
2748 _editor->snap_to_with_modifier (cx_frames, event);
2751 cx_frames = min (cx_frames, _point->line().maximum_time());
2753 float const fraction = 1.0 - (cy / _point->line().height());
2755 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2757 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2759 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2763 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2765 if (!movement_occurred) {
2769 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2770 _editor->reset_point_selection ();
2774 motion (event, false);
2777 _point->line().end_drag ();
2778 _editor->session()->commit_reversible_command ();
2782 ControlPointDrag::aborted (bool)
2784 _point->line().reset ();
2788 ControlPointDrag::active (Editing::MouseMode m)
2790 if (m == Editing::MouseGain) {
2791 /* always active in mouse gain */
2795 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2796 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2799 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2802 _cumulative_y_drag (0)
2804 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2808 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2810 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2813 _item = &_line->grab_item ();
2815 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2816 origin, and ditto for y.
2819 double cx = event->button.x;
2820 double cy = event->button.y;
2822 _line->parent_group().w2i (cx, cy);
2824 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2829 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2830 /* no adjacent points */
2834 Drag::start_grab (event, _editor->cursors()->fader);
2836 /* store grab start in parent frame */
2841 double fraction = 1.0 - (cy / _line->height());
2843 _line->start_drag_line (before, after, fraction);
2845 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2846 event->button.x + 10, event->button.y + 10);
2848 _editor->show_verbose_canvas_cursor ();
2852 LineDrag::motion (GdkEvent* event, bool)
2854 double dy = _drags->current_pointer_y() - last_pointer_y();
2856 if (event->button.state & Keyboard::SecondaryModifier) {
2860 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2862 _cumulative_y_drag = cy - _fixed_grab_y;
2865 cy = min ((double) _line->height(), cy);
2867 double const fraction = 1.0 - (cy / _line->height());
2871 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2877 /* we are ignoring x position for this drag, so we can just pass in anything */
2878 _line->drag_motion (0, fraction, true, push);
2880 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2884 LineDrag::finished (GdkEvent* event, bool)
2886 motion (event, false);
2888 _editor->session()->commit_reversible_command ();
2892 LineDrag::aborted (bool)
2897 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2900 _cumulative_x_drag (0)
2902 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2906 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2908 Drag::start_grab (event);
2910 _line = reinterpret_cast<Line*> (_item);
2913 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2915 double cx = event->button.x;
2916 double cy = event->button.y;
2918 _item->property_parent().get_value()->w2i(cx, cy);
2920 /* store grab start in parent frame */
2921 _region_view_grab_x = cx;
2923 _before = *(float*) _item->get_data ("position");
2925 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2927 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2931 FeatureLineDrag::motion (GdkEvent*, bool)
2933 double dx = _drags->current_pointer_x() - last_pointer_x();
2935 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2937 _cumulative_x_drag += dx;
2939 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2948 ArdourCanvas::Points points;
2950 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
2952 _line->get_bounds(x1, y2, x2, y2);
2954 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
2955 points.push_back(Gnome::Art::Point(cx, y2 - y1));
2957 _line->property_points() = points;
2959 float *pos = new float;
2962 _line->set_data ("position", pos);
2968 FeatureLineDrag::finished (GdkEvent*, bool)
2970 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2971 _arv->update_transient(_before, _before);
2975 FeatureLineDrag::aborted (bool)
2980 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2983 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2987 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2989 Drag::start_grab (event);
2990 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2994 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3001 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3003 framepos_t grab = grab_frame ();
3004 if (Config->get_rubberbanding_snaps_to_grid ()) {
3005 _editor->snap_to_with_modifier (grab, event);
3008 /* base start and end on initial click position */
3018 if (_drags->current_pointer_y() < grab_y()) {
3019 y1 = _drags->current_pointer_y();
3022 y2 = _drags->current_pointer_y();
3027 if (start != end || y1 != y2) {
3029 double x1 = _editor->frame_to_pixel (start);
3030 double x2 = _editor->frame_to_pixel (end);
3032 _editor->rubberband_rect->property_x1() = x1;
3033 _editor->rubberband_rect->property_y1() = y1;
3034 _editor->rubberband_rect->property_x2() = x2;
3035 _editor->rubberband_rect->property_y2() = y2;
3037 _editor->rubberband_rect->show();
3038 _editor->rubberband_rect->raise_to_top();
3040 _editor->show_verbose_time_cursor (pf, 10);
3045 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3047 if (movement_occurred) {
3049 motion (event, false);
3052 if (_drags->current_pointer_y() < grab_y()) {
3053 y1 = _drags->current_pointer_y();
3056 y2 = _drags->current_pointer_y();
3061 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3063 _editor->begin_reversible_command (_("rubberband selection"));
3065 if (grab_frame() < last_pointer_frame()) {
3066 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3068 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3071 _editor->commit_reversible_command ();
3074 if (!getenv("ARDOUR_SAE")) {
3075 _editor->selection->clear_tracks();
3077 _editor->selection->clear_regions();
3078 _editor->selection->clear_points ();
3079 _editor->selection->clear_lines ();
3082 _editor->rubberband_rect->hide();
3086 RubberbandSelectDrag::aborted (bool)
3088 _editor->rubberband_rect->hide ();
3091 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3092 : RegionDrag (e, i, p, v)
3094 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3098 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3100 Drag::start_grab (event, cursor);
3102 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3106 TimeFXDrag::motion (GdkEvent* event, bool)
3108 RegionView* rv = _primary;
3110 framepos_t const pf = adjusted_current_frame (event);
3112 if (pf > rv->region()->position()) {
3113 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3116 _editor->show_verbose_time_cursor (pf, 10);
3120 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3122 _primary->get_time_axis_view().hide_timestretch ();
3124 if (!movement_occurred) {
3128 if (last_pointer_frame() < _primary->region()->position()) {
3129 /* backwards drag of the left edge - not usable */
3133 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3135 float percentage = (double) newlen / (double) _primary->region()->length();
3137 #ifndef USE_RUBBERBAND
3138 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3139 if (_primary->region()->data_type() == DataType::AUDIO) {
3140 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3144 _editor->begin_reversible_command (_("timestretch"));
3146 // XXX how do timeFX on multiple regions ?
3151 if (_editor->time_stretch (rs, percentage) == -1) {
3152 error << _("An error occurred while executing time stretch operation") << endmsg;
3157 TimeFXDrag::aborted (bool)
3159 _primary->get_time_axis_view().hide_timestretch ();
3162 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3165 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3169 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3171 Drag::start_grab (event);
3175 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3177 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3181 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3183 if (movement_occurred && _editor->session()) {
3184 /* make sure we stop */
3185 _editor->session()->request_transport_speed (0.0);
3190 ScrubDrag::aborted (bool)
3195 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3199 , _original_pointer_time_axis (-1)
3200 , _last_pointer_time_axis (-1)
3202 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3206 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3208 if (_editor->session() == 0) {
3212 Gdk::Cursor* cursor = 0;
3214 switch (_operation) {
3215 case CreateSelection:
3216 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3221 cursor = _editor->cursors()->selector;
3222 Drag::start_grab (event, cursor);
3225 case SelectionStartTrim:
3226 if (_editor->clicked_axisview) {
3227 _editor->clicked_axisview->order_selection_trims (_item, true);
3229 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3232 case SelectionEndTrim:
3233 if (_editor->clicked_axisview) {
3234 _editor->clicked_axisview->order_selection_trims (_item, false);
3236 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3240 Drag::start_grab (event, cursor);
3244 if (_operation == SelectionMove) {
3245 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3247 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3250 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3254 SelectionDrag::setup_pointer_frame_offset ()
3256 switch (_operation) {
3257 case CreateSelection:
3258 _pointer_frame_offset = 0;
3261 case SelectionStartTrim:
3263 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3266 case SelectionEndTrim:
3267 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3273 SelectionDrag::motion (GdkEvent* event, bool first_move)
3275 framepos_t start = 0;
3279 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3280 if (pending_time_axis.first == 0) {
3284 framepos_t const pending_position = adjusted_current_frame (event);
3286 /* only alter selection if things have changed */
3288 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3292 switch (_operation) {
3293 case CreateSelection:
3295 framepos_t grab = grab_frame ();
3298 _editor->snap_to (grab);
3301 if (pending_position < grab_frame()) {
3302 start = pending_position;
3305 end = pending_position;
3309 /* first drag: Either add to the selection
3310 or create a new selection
3316 /* adding to the selection */
3317 _editor->set_selected_track_as_side_effect (Selection::Add);
3318 //_editor->selection->add (_editor->clicked_axisview);
3319 _editor->clicked_selection = _editor->selection->add (start, end);
3324 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3325 //_editor->selection->set (_editor->clicked_axisview);
3326 _editor->set_selected_track_as_side_effect (Selection::Set);
3329 _editor->clicked_selection = _editor->selection->set (start, end);
3333 /* select the track that we're in */
3334 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3335 // _editor->set_selected_track_as_side_effect (Selection::Add);
3336 _editor->selection->add (pending_time_axis.first);
3337 _added_time_axes.push_back (pending_time_axis.first);
3340 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3341 tracks that we selected in the first place.
3344 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3345 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3347 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3348 while (i != _added_time_axes.end()) {
3350 list<TimeAxisView*>::iterator tmp = i;
3353 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3354 _editor->selection->remove (*i);
3355 _added_time_axes.remove (*i);
3364 case SelectionStartTrim:
3366 start = _editor->selection->time[_editor->clicked_selection].start;
3367 end = _editor->selection->time[_editor->clicked_selection].end;
3369 if (pending_position > end) {
3372 start = pending_position;
3376 case SelectionEndTrim:
3378 start = _editor->selection->time[_editor->clicked_selection].start;
3379 end = _editor->selection->time[_editor->clicked_selection].end;
3381 if (pending_position < start) {
3384 end = pending_position;
3391 start = _editor->selection->time[_editor->clicked_selection].start;
3392 end = _editor->selection->time[_editor->clicked_selection].end;
3394 length = end - start;
3396 start = pending_position;
3397 _editor->snap_to (start);
3399 end = start + length;
3404 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3405 _editor->start_canvas_autoscroll (1, 0);
3409 _editor->selection->replace (_editor->clicked_selection, start, end);
3412 if (_operation == SelectionMove) {
3413 _editor->show_verbose_time_cursor(start, 10);
3415 _editor->show_verbose_time_cursor(pending_position, 10);
3420 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3422 Session* s = _editor->session();
3424 if (movement_occurred) {
3425 motion (event, false);
3426 /* XXX this is not object-oriented programming at all. ick */
3427 if (_editor->selection->time.consolidate()) {
3428 _editor->selection->TimeChanged ();
3431 /* XXX what if its a music time selection? */
3432 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3433 s->request_play_range (&_editor->selection->time, true);
3438 /* just a click, no pointer movement.*/
3440 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3441 _editor->selection->clear_time();
3444 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3445 _editor->selection->set (_editor->clicked_axisview);
3448 if (s && s->get_play_range () && s->transport_rolling()) {
3449 s->request_stop (false, false);
3454 _editor->stop_canvas_autoscroll ();
3458 SelectionDrag::aborted (bool)
3463 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3468 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3470 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3471 physical_screen_height (_editor->get_window()));
3472 _drag_rect->hide ();
3474 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3475 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3479 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3481 if (_editor->session() == 0) {
3485 Gdk::Cursor* cursor = 0;
3487 if (!_editor->temp_location) {
3488 _editor->temp_location = new Location (*_editor->session());
3491 switch (_operation) {
3492 case CreateRangeMarker:
3493 case CreateTransportMarker:
3494 case CreateCDMarker:
3496 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3501 cursor = _editor->cursors()->selector;
3505 Drag::start_grab (event, cursor);
3507 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3511 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3513 framepos_t start = 0;
3515 ArdourCanvas::SimpleRect *crect;
3517 switch (_operation) {
3518 case CreateRangeMarker:
3519 crect = _editor->range_bar_drag_rect;
3521 case CreateTransportMarker:
3522 crect = _editor->transport_bar_drag_rect;
3524 case CreateCDMarker:
3525 crect = _editor->cd_marker_bar_drag_rect;
3528 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3533 framepos_t const pf = adjusted_current_frame (event);
3535 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3536 framepos_t grab = grab_frame ();
3537 _editor->snap_to (grab);
3539 if (pf < grab_frame()) {
3547 /* first drag: Either add to the selection
3548 or create a new selection.
3553 _editor->temp_location->set (start, end);
3557 update_item (_editor->temp_location);
3559 //_drag_rect->raise_to_top();
3564 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3565 _editor->start_canvas_autoscroll (1, 0);
3569 _editor->temp_location->set (start, end);
3571 double x1 = _editor->frame_to_pixel (start);
3572 double x2 = _editor->frame_to_pixel (end);
3573 crect->property_x1() = x1;
3574 crect->property_x2() = x2;
3576 update_item (_editor->temp_location);
3579 _editor->show_verbose_time_cursor (pf, 10);
3584 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3586 Location * newloc = 0;
3590 if (movement_occurred) {
3591 motion (event, false);
3594 switch (_operation) {
3595 case CreateRangeMarker:
3596 case CreateCDMarker:
3598 _editor->begin_reversible_command (_("new range marker"));
3599 XMLNode &before = _editor->session()->locations()->get_state();
3600 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3601 if (_operation == CreateCDMarker) {
3602 flags = Location::IsRangeMarker | Location::IsCDMarker;
3603 _editor->cd_marker_bar_drag_rect->hide();
3606 flags = Location::IsRangeMarker;
3607 _editor->range_bar_drag_rect->hide();
3609 newloc = new Location (
3610 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3613 _editor->session()->locations()->add (newloc, true);
3614 XMLNode &after = _editor->session()->locations()->get_state();
3615 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3616 _editor->commit_reversible_command ();
3620 case CreateTransportMarker:
3621 // popup menu to pick loop or punch
3622 _editor->new_transport_marker_context_menu (&event->button, _item);
3626 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3628 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3633 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3635 if (end == max_framepos) {
3636 end = _editor->session()->current_end_frame ();
3639 if (start == max_framepos) {
3640 start = _editor->session()->current_start_frame ();
3643 switch (_editor->mouse_mode) {
3645 /* find the two markers on either side and then make the selection from it */
3646 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3650 /* find the two markers on either side of the click and make the range out of it */
3651 _editor->selection->set (start, end);
3660 _editor->stop_canvas_autoscroll ();
3664 RangeMarkerBarDrag::aborted (bool)
3670 RangeMarkerBarDrag::update_item (Location* location)
3672 double const x1 = _editor->frame_to_pixel (location->start());
3673 double const x2 = _editor->frame_to_pixel (location->end());
3675 _drag_rect->property_x1() = x1;
3676 _drag_rect->property_x2() = x2;
3679 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3683 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3687 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3689 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3690 Drag::start_grab (event, _editor->cursors()->zoom_out);
3693 Drag::start_grab (event, _editor->cursors()->zoom_in);
3697 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3701 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3706 framepos_t const pf = adjusted_current_frame (event);
3708 framepos_t grab = grab_frame ();
3709 _editor->snap_to_with_modifier (grab, event);
3711 /* base start and end on initial click position */
3723 _editor->zoom_rect->show();
3724 _editor->zoom_rect->raise_to_top();
3727 _editor->reposition_zoom_rect(start, end);
3729 _editor->show_verbose_time_cursor (pf, 10);
3734 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3736 if (movement_occurred) {
3737 motion (event, false);
3739 if (grab_frame() < last_pointer_frame()) {
3740 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3742 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3745 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3746 _editor->tav_zoom_step (_zoom_out);
3748 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3752 _editor->zoom_rect->hide();
3756 MouseZoomDrag::aborted (bool)
3758 _editor->zoom_rect->hide ();
3761 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3763 , _cumulative_dx (0)
3764 , _cumulative_dy (0)
3766 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3768 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3769 _region = &_primary->region_view ();
3770 _note_height = _region->midi_stream_view()->note_height ();
3774 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3776 Drag::start_grab (event);
3778 if (!(_was_selected = _primary->selected())) {
3780 /* tertiary-click means extend selection - we'll do that on button release,
3781 so don't add it here, because otherwise we make it hard to figure
3782 out the "extend-to" range.
3785 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3788 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3791 _region->note_selected (_primary, true);
3793 _region->unique_select (_primary);
3799 /** @return Current total drag x change in frames */
3801 NoteDrag::total_dx () const
3804 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3806 /* primary note time */
3807 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3809 /* new time of the primary note relative to the region position */
3810 frameoffset_t const st = n + dx;
3812 /* snap and return corresponding delta */
3813 return _region->snap_frame_to_frame (st) - n;
3816 /** @return Current total drag y change in notes */
3818 NoteDrag::total_dy () const
3820 /* this is `backwards' to make increasing note number go in the right direction */
3821 double const dy = _drags->current_pointer_y() - grab_y();
3826 if (abs (dy) >= _note_height) {
3828 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3830 ndy = (int8_t) floor (dy / _note_height / 2.0);
3834 /* more positive value = higher pitch and higher y-axis position on track,
3835 which is the inverse of the X-centric geometric universe
3842 NoteDrag::motion (GdkEvent *, bool)
3844 /* Total change in x and y since the start of the drag */
3845 frameoffset_t const dx = total_dx ();
3846 int8_t const dy = -total_dy ();
3848 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3849 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3850 double const tdy = dy * _note_height - _cumulative_dy;
3853 _cumulative_dx += tdx;
3854 _cumulative_dy += tdy;
3856 int8_t note_delta = total_dy();
3858 _region->move_selection (tdx, tdy, note_delta);
3861 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3862 (int) floor (_primary->note()->note() + note_delta));
3864 _editor->show_verbose_canvas_cursor_with (buf);
3869 NoteDrag::finished (GdkEvent* ev, bool moved)
3872 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3874 if (_was_selected) {
3875 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3877 _region->note_deselected (_primary);
3880 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3881 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3883 if (!extend && !add && _region->selection_size() > 1) {
3884 _region->unique_select (_primary);
3885 } else if (extend) {
3886 _region->note_selected (_primary, true, true);
3888 /* it was added during button press */
3893 _region->note_dropped (_primary, total_dx(), total_dy());
3898 NoteDrag::aborted (bool)
3903 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3904 : Drag (editor, item)
3906 , _nothing_to_drag (false)
3908 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3910 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3913 /* get all lines in the automation view */
3914 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3916 /* find those that overlap the ranges being dragged */
3917 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3918 while (i != lines.end ()) {
3919 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3922 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3924 /* check this range against all the AudioRanges that we are using */
3925 list<AudioRange>::const_iterator k = _ranges.begin ();
3926 while (k != _ranges.end()) {
3927 if (k->coverage (r.first, r.second) != OverlapNone) {
3933 /* add it to our list if it overlaps at all */
3934 if (k != _ranges.end()) {
3939 _lines.push_back (n);
3945 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3949 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3951 Drag::start_grab (event, cursor);
3953 /* Get line states before we start changing things */
3954 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3955 i->state = &i->line->get_state ();
3958 if (_ranges.empty()) {
3960 /* No selected time ranges: drag all points */
3961 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3962 uint32_t const N = i->line->npoints ();
3963 for (uint32_t j = 0; j < N; ++j) {
3964 i->points.push_back (i->line->nth (j));
3970 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3972 framecnt_t const half = (i->start + i->end) / 2;
3974 /* find the line that this audio range starts in */
3975 list<Line>::iterator j = _lines.begin();
3976 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3980 if (j != _lines.end()) {
3981 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3983 /* j is the line that this audio range starts in; fade into it;
3984 64 samples length plucked out of thin air.
3987 framepos_t a = i->start + 64;
3992 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3993 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3995 the_list->add (p, the_list->eval (p));
3996 j->line->add_always_in_view (p);
3997 the_list->add (q, the_list->eval (q));
3998 j->line->add_always_in_view (q);
4001 /* same thing for the end */
4004 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4008 if (j != _lines.end()) {
4009 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4011 /* j is the line that this audio range starts in; fade out of it;
4012 64 samples length plucked out of thin air.
4015 framepos_t b = i->end - 64;
4020 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4021 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4023 the_list->add (p, the_list->eval (p));
4024 j->line->add_always_in_view (p);
4025 the_list->add (q, the_list->eval (q));
4026 j->line->add_always_in_view (q);
4030 _nothing_to_drag = true;
4032 /* Find all the points that should be dragged and put them in the relevant
4033 points lists in the Line structs.
4036 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4038 uint32_t const N = i->line->npoints ();
4039 for (uint32_t j = 0; j < N; ++j) {
4041 /* here's a control point on this line */
4042 ControlPoint* p = i->line->nth (j);
4043 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4045 /* see if it's inside a range */
4046 list<AudioRange>::const_iterator k = _ranges.begin ();
4047 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4051 if (k != _ranges.end()) {
4052 /* dragging this point */
4053 _nothing_to_drag = false;
4054 i->points.push_back (p);
4060 if (_nothing_to_drag) {
4064 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4065 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4070 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4072 if (_nothing_to_drag) {
4076 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4077 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4079 /* we are ignoring x position for this drag, so we can just pass in anything */
4080 i->line->drag_motion (0, f, true, false);
4085 AutomationRangeDrag::finished (GdkEvent* event, bool)
4087 if (_nothing_to_drag) {
4091 motion (event, false);
4092 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4093 i->line->end_drag ();
4094 i->line->clear_always_in_view ();
4097 _editor->session()->commit_reversible_command ();
4101 AutomationRangeDrag::aborted (bool)
4103 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4104 i->line->clear_always_in_view ();
4109 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4112 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4113 layer = v->region()->layer ();
4114 initial_y = v->get_canvas_group()->property_y ();
4115 initial_playlist = v->region()->playlist ();
4116 initial_position = v->region()->position ();
4117 initial_end = v->region()->position () + v->region()->length ();
4120 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4124 , _cumulative_dx (0)
4126 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4130 PatchChangeDrag::motion (GdkEvent* ev, bool)
4132 framepos_t f = adjusted_current_frame (ev);
4133 boost::shared_ptr<Region> r = _region_view->region ();
4134 f = max (f, r->position ());
4135 f = min (f, r->last_frame ());
4137 framecnt_t const dxf = f - grab_frame();
4138 double const dxu = _editor->frame_to_unit (dxf);
4139 _patch_change->move (dxu - _cumulative_dx, 0);
4140 _cumulative_dx = dxu;
4144 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4146 if (!movement_occurred) {
4150 boost::shared_ptr<Region> r (_region_view->region ());
4152 framepos_t f = adjusted_current_frame (ev);
4153 f = max (f, r->position ());
4154 f = min (f, r->last_frame ());
4156 _region_view->move_patch_change (
4158 _region_view->frames_to_beats (f - r->position() - r->start())
4163 PatchChangeDrag::aborted (bool)
4165 _patch_change->move (-_cumulative_dx, 0);
4169 PatchChangeDrag::setup_pointer_frame_offset ()
4171 boost::shared_ptr<Region> region = _region_view->region ();
4172 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();