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)
1364 framepos_t const f = adjusted_current_frame (event);
1365 if (f < grab_frame()) {
1366 _region->set_position (f, this);
1369 /* again, don't use a zero-length region (see above) */
1370 framecnt_t const len = abs (f - grab_frame ());
1371 _region->set_length (len < 1 ? 1 : len, this);
1377 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1379 if (!movement_occurred) {
1384 _editor->commit_reversible_command ();
1389 RegionCreateDrag::add_region ()
1391 if (_editor->session()) {
1392 const TempoMap& map (_editor->session()->tempo_map());
1393 framecnt_t pos = grab_frame();
1394 const Meter& m = map.meter_at (pos);
1395 /* not that the frame rate used here can be affected by pull up/down which
1398 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1399 _region = _view->add_region (grab_frame(), len, false);
1404 RegionCreateDrag::aborted (bool)
1409 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1413 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1417 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1419 Gdk::Cursor* cursor;
1420 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1421 float x_fraction = cnote->mouse_x_fraction ();
1423 if (x_fraction > 0.0 && x_fraction < 0.25) {
1424 cursor = _editor->cursors()->left_side_trim;
1426 cursor = _editor->cursors()->right_side_trim;
1429 Drag::start_grab (event, cursor);
1431 region = &cnote->region_view();
1433 double const region_start = region->get_position_pixels();
1434 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1436 if (grab_x() <= middle_point) {
1437 cursor = _editor->cursors()->left_side_trim;
1440 cursor = _editor->cursors()->right_side_trim;
1444 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1446 if (event->motion.state & Keyboard::PrimaryModifier) {
1452 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1454 if (ms.size() > 1) {
1455 /* has to be relative, may make no sense otherwise */
1459 /* select this note; if it is already selected, preserve the existing selection,
1460 otherwise make this note the only one selected.
1462 region->note_selected (cnote, cnote->selected ());
1464 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1465 MidiRegionSelection::iterator next;
1468 (*r)->begin_resizing (at_front);
1474 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1476 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1477 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1478 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1483 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1485 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1486 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1487 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1492 NoteResizeDrag::aborted (bool)
1497 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1500 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1504 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1510 RegionGainDrag::finished (GdkEvent *, bool)
1516 RegionGainDrag::aborted (bool)
1521 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1522 : RegionDrag (e, i, p, v)
1524 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1528 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1531 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1532 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1534 if (tv && tv->is_track()) {
1535 speed = tv->track()->speed();
1538 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1539 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1540 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1542 framepos_t const pf = adjusted_current_frame (event);
1544 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1545 /* Move the contents of the region around without changing the region bounds */
1546 _operation = ContentsTrim;
1547 Drag::start_grab (event, _editor->cursors()->trimmer);
1549 /* These will get overridden for a point trim.*/
1550 if (pf < (region_start + region_length/2)) {
1551 /* closer to front */
1552 _operation = StartTrim;
1553 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1556 _operation = EndTrim;
1557 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1561 switch (_operation) {
1563 _editor->show_verbose_time_cursor (region_start, 10);
1564 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1565 i->view->trim_front_starting ();
1569 _editor->show_verbose_time_cursor (region_end, 10);
1572 _editor->show_verbose_time_cursor (pf, 10);
1576 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1577 i->view->region()->suspend_property_changes ();
1582 TrimDrag::motion (GdkEvent* event, bool first_move)
1584 RegionView* rv = _primary;
1587 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1588 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1589 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1591 if (tv && tv->is_track()) {
1592 speed = tv->track()->speed();
1595 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1601 switch (_operation) {
1603 trim_type = "Region start trim";
1606 trim_type = "Region end trim";
1609 trim_type = "Region content trim";
1613 _editor->begin_reversible_command (trim_type);
1615 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1616 RegionView* rv = i->view;
1617 rv->fake_set_opaque (false);
1618 rv->enable_display (false);
1619 rv->region()->playlist()->clear_owned_changes ();
1621 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1624 arv->temporarily_hide_envelope ();
1627 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1628 insert_result = _editor->motion_frozen_playlists.insert (pl);
1630 if (insert_result.second) {
1636 bool non_overlap_trim = false;
1638 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1639 non_overlap_trim = true;
1642 switch (_operation) {
1644 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1645 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1650 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1651 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1657 bool swap_direction = false;
1659 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1660 swap_direction = true;
1663 framecnt_t frame_delta = 0;
1665 bool left_direction = false;
1666 if (last_pointer_frame() > adjusted_current_frame(event)) {
1667 left_direction = true;
1670 if (left_direction) {
1671 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1673 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1676 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1677 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1683 switch (_operation) {
1685 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1688 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1691 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1698 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1700 if (movement_occurred) {
1701 motion (event, false);
1703 /* This must happen before the region's StatefulDiffCommand is created, as it may
1704 `correct' (ahem) the region's _start from being negative to being zero. It
1705 needs to be zero in the undo record.
1707 if (_operation == StartTrim) {
1708 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1709 i->view->trim_front_ending ();
1713 if (!_editor->selection->selected (_primary)) {
1714 _primary->thaw_after_trim ();
1717 set<boost::shared_ptr<Playlist> > diffed_playlists;
1719 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1720 i->view->thaw_after_trim ();
1721 i->view->enable_display (true);
1722 i->view->fake_set_opaque (true);
1724 /* Trimming one region may affect others on the playlist, so we need
1725 to get undo Commands from the whole playlist rather than just the
1726 region. Use diffed_playlists to make sure we don't diff a given
1727 playlist more than once.
1729 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1730 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1731 vector<Command*> cmds;
1733 _editor->session()->add_commands (cmds);
1734 diffed_playlists.insert (p);
1738 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1742 _editor->motion_frozen_playlists.clear ();
1743 _editor->commit_reversible_command();
1746 /* no mouse movement */
1747 _editor->point_trim (event, adjusted_current_frame (event));
1750 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1751 if (_operation == StartTrim) {
1752 i->view->trim_front_ending ();
1755 i->view->region()->resume_property_changes ();
1760 TrimDrag::aborted (bool movement_occurred)
1762 /* Our motion method is changing model state, so use the Undo system
1763 to cancel. Perhaps not ideal, as this will leave an Undo point
1764 behind which may be slightly odd from the user's point of view.
1769 if (movement_occurred) {
1773 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1774 i->view->region()->resume_property_changes ();
1779 TrimDrag::setup_pointer_frame_offset ()
1781 list<DraggingView>::iterator i = _views.begin ();
1782 while (i != _views.end() && i->view != _primary) {
1786 if (i == _views.end()) {
1790 switch (_operation) {
1792 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1795 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1802 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1806 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1808 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1813 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1816 // create a dummy marker for visual representation of moving the copy.
1817 // The actual copying is not done before we reach the finish callback.
1819 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1821 MeterMarker* new_marker = new MeterMarker (
1823 *_editor->meter_group,
1824 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1826 *new MeterSection (_marker->meter())
1829 _item = &new_marker->the_item ();
1830 _marker = new_marker;
1834 MetricSection& section (_marker->meter());
1836 if (!section.movable()) {
1842 Drag::start_grab (event, cursor);
1844 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1848 MeterMarkerDrag::setup_pointer_frame_offset ()
1850 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1854 MeterMarkerDrag::motion (GdkEvent* event, bool)
1856 framepos_t const pf = adjusted_current_frame (event);
1858 _marker->set_position (pf);
1860 _editor->show_verbose_time_cursor (pf, 10);
1864 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1866 if (!movement_occurred) {
1870 motion (event, false);
1872 Timecode::BBT_Time when;
1874 TempoMap& map (_editor->session()->tempo_map());
1875 map.bbt_time (last_pointer_frame(), when);
1877 if (_copy == true) {
1878 _editor->begin_reversible_command (_("copy meter mark"));
1879 XMLNode &before = map.get_state();
1880 map.add_meter (_marker->meter(), when);
1881 XMLNode &after = map.get_state();
1882 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1883 _editor->commit_reversible_command ();
1885 // delete the dummy marker we used for visual representation of copying.
1886 // a new visual marker will show up automatically.
1889 _editor->begin_reversible_command (_("move meter mark"));
1890 XMLNode &before = map.get_state();
1891 map.move_meter (_marker->meter(), when);
1892 XMLNode &after = map.get_state();
1893 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1894 _editor->commit_reversible_command ();
1899 MeterMarkerDrag::aborted (bool)
1901 _marker->set_position (_marker->meter().frame ());
1904 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1908 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1910 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1915 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1919 // create a dummy marker for visual representation of moving the copy.
1920 // The actual copying is not done before we reach the finish callback.
1922 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1924 TempoMarker* new_marker = new TempoMarker (
1926 *_editor->tempo_group,
1927 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1929 *new TempoSection (_marker->tempo())
1932 _item = &new_marker->the_item ();
1933 _marker = new_marker;
1937 Drag::start_grab (event, cursor);
1939 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1943 TempoMarkerDrag::setup_pointer_frame_offset ()
1945 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1949 TempoMarkerDrag::motion (GdkEvent* event, bool)
1951 framepos_t const pf = adjusted_current_frame (event);
1952 _marker->set_position (pf);
1953 _editor->show_verbose_time_cursor (pf, 10);
1957 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1959 if (!movement_occurred) {
1963 motion (event, false);
1965 Timecode::BBT_Time when;
1967 TempoMap& map (_editor->session()->tempo_map());
1968 map.bbt_time (last_pointer_frame(), when);
1970 if (_copy == true) {
1971 _editor->begin_reversible_command (_("copy tempo mark"));
1972 XMLNode &before = map.get_state();
1973 map.add_tempo (_marker->tempo(), when);
1974 XMLNode &after = map.get_state();
1975 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1976 _editor->commit_reversible_command ();
1978 // delete the dummy marker we used for visual representation of copying.
1979 // a new visual marker will show up automatically.
1982 _editor->begin_reversible_command (_("move tempo mark"));
1983 XMLNode &before = map.get_state();
1984 map.move_tempo (_marker->tempo(), when);
1985 XMLNode &after = map.get_state();
1986 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1987 _editor->commit_reversible_command ();
1992 TempoMarkerDrag::aborted (bool)
1994 _marker->set_position (_marker->tempo().frame());
1997 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2001 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2004 /** Do all the things we do when dragging the playhead to make it look as though
2005 * we have located, without actually doing the locate (because that would cause
2006 * the diskstream buffers to be refilled, which is too slow).
2009 CursorDrag::fake_locate (framepos_t t)
2011 _editor->playhead_cursor->set_position (t);
2013 Session* s = _editor->session ();
2014 if (s->timecode_transmission_suspended ()) {
2015 framepos_t const f = _editor->playhead_cursor->current_frame;
2016 s->send_mmc_locate (f);
2017 s->send_full_time_code (f);
2020 _editor->show_verbose_time_cursor (t, 10);
2021 _editor->UpdateAllTransportClocks (t);
2025 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2027 Drag::start_grab (event, c);
2029 framepos_t where = _editor->event_frame (event, 0, 0);
2030 _editor->snap_to_with_modifier (where, event);
2032 _editor->_dragging_playhead = true;
2034 Session* s = _editor->session ();
2037 if (_was_rolling && _stop) {
2041 if (s->is_auditioning()) {
2042 s->cancel_audition ();
2045 s->request_suspend_timecode_transmission ();
2046 while (!s->timecode_transmission_suspended ()) {
2047 /* twiddle our thumbs */
2051 fake_locate (where);
2055 CursorDrag::motion (GdkEvent* event, bool)
2057 framepos_t const adjusted_frame = adjusted_current_frame (event);
2059 if (adjusted_frame == last_pointer_frame()) {
2063 fake_locate (adjusted_frame);
2066 _editor->update_canvas_now ();
2071 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2073 _editor->_dragging_playhead = false;
2075 if (!movement_occurred && _stop) {
2079 motion (event, false);
2081 Session* s = _editor->session ();
2083 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2084 _editor->_pending_locate_request = true;
2085 s->request_resume_timecode_transmission ();
2090 CursorDrag::aborted (bool)
2092 if (_editor->_dragging_playhead) {
2093 _editor->session()->request_resume_timecode_transmission ();
2094 _editor->_dragging_playhead = false;
2097 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2100 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2101 : RegionDrag (e, i, p, v)
2103 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2107 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2109 Drag::start_grab (event, cursor);
2111 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2112 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2114 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2116 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2120 FadeInDrag::setup_pointer_frame_offset ()
2122 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2123 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2124 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2128 FadeInDrag::motion (GdkEvent* event, bool)
2130 framecnt_t fade_length;
2132 framepos_t const pos = adjusted_current_frame (event);
2134 boost::shared_ptr<Region> region = _primary->region ();
2136 if (pos < (region->position() + 64)) {
2137 fade_length = 64; // this should be a minimum defined somewhere
2138 } else if (pos > region->last_frame()) {
2139 fade_length = region->length();
2141 fade_length = pos - region->position();
2144 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2146 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2152 tmp->reset_fade_in_shape_width (fade_length);
2153 tmp->show_fade_line((framecnt_t) fade_length);
2156 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2160 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2162 if (!movement_occurred) {
2166 framecnt_t fade_length;
2168 framepos_t const pos = adjusted_current_frame (event);
2170 boost::shared_ptr<Region> region = _primary->region ();
2172 if (pos < (region->position() + 64)) {
2173 fade_length = 64; // this should be a minimum defined somewhere
2174 } else if (pos > region->last_frame()) {
2175 fade_length = region->length();
2177 fade_length = pos - region->position();
2180 _editor->begin_reversible_command (_("change fade in length"));
2182 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2184 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2190 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2191 XMLNode &before = alist->get_state();
2193 tmp->audio_region()->set_fade_in_length (fade_length);
2194 tmp->audio_region()->set_fade_in_active (true);
2195 tmp->hide_fade_line();
2197 XMLNode &after = alist->get_state();
2198 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2201 _editor->commit_reversible_command ();
2205 FadeInDrag::aborted (bool)
2207 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2208 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2214 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2215 tmp->hide_fade_line();
2219 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2220 : RegionDrag (e, i, p, v)
2222 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2226 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2228 Drag::start_grab (event, cursor);
2230 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2231 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2233 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2235 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2239 FadeOutDrag::setup_pointer_frame_offset ()
2241 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2242 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2243 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2247 FadeOutDrag::motion (GdkEvent* event, bool)
2249 framecnt_t fade_length;
2251 framepos_t const pos = adjusted_current_frame (event);
2253 boost::shared_ptr<Region> region = _primary->region ();
2255 if (pos > (region->last_frame() - 64)) {
2256 fade_length = 64; // this should really be a minimum fade defined somewhere
2258 else if (pos < region->position()) {
2259 fade_length = region->length();
2262 fade_length = region->last_frame() - pos;
2265 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2267 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2273 tmp->reset_fade_out_shape_width (fade_length);
2274 tmp->show_fade_line(region->length() - fade_length);
2277 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2281 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2283 if (!movement_occurred) {
2287 framecnt_t fade_length;
2289 framepos_t const pos = adjusted_current_frame (event);
2291 boost::shared_ptr<Region> region = _primary->region ();
2293 if (pos > (region->last_frame() - 64)) {
2294 fade_length = 64; // this should really be a minimum fade defined somewhere
2296 else if (pos < region->position()) {
2297 fade_length = region->length();
2300 fade_length = region->last_frame() - pos;
2303 _editor->begin_reversible_command (_("change fade out length"));
2305 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2307 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2313 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2314 XMLNode &before = alist->get_state();
2316 tmp->audio_region()->set_fade_out_length (fade_length);
2317 tmp->audio_region()->set_fade_out_active (true);
2318 tmp->hide_fade_line();
2320 XMLNode &after = alist->get_state();
2321 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2324 _editor->commit_reversible_command ();
2328 FadeOutDrag::aborted (bool)
2330 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2331 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2337 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2338 tmp->hide_fade_line();
2342 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2345 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2347 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2350 _points.push_back (Gnome::Art::Point (0, 0));
2351 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2354 MarkerDrag::~MarkerDrag ()
2356 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2362 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2364 Drag::start_grab (event, cursor);
2368 Location *location = _editor->find_location_from_marker (_marker, is_start);
2369 _editor->_dragging_edit_point = true;
2371 update_item (location);
2373 // _drag_line->show();
2374 // _line->raise_to_top();
2377 _editor->show_verbose_time_cursor (location->start(), 10);
2379 _editor->show_verbose_time_cursor (location->end(), 10);
2382 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2385 case Selection::Toggle:
2386 _editor->selection->toggle (_marker);
2388 case Selection::Set:
2389 if (!_editor->selection->selected (_marker)) {
2390 _editor->selection->set (_marker);
2393 case Selection::Extend:
2395 Locations::LocationList ll;
2396 list<Marker*> to_add;
2398 _editor->selection->markers.range (s, e);
2399 s = min (_marker->position(), s);
2400 e = max (_marker->position(), e);
2403 if (e < max_framepos) {
2406 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2407 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2408 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2411 to_add.push_back (lm->start);
2414 to_add.push_back (lm->end);
2418 if (!to_add.empty()) {
2419 _editor->selection->add (to_add);
2423 case Selection::Add:
2424 _editor->selection->add (_marker);
2428 /* Set up copies for us to manipulate during the drag */
2430 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2431 Location* l = _editor->find_location_from_marker (*i, is_start);
2432 _copied_locations.push_back (new Location (*l));
2437 MarkerDrag::setup_pointer_frame_offset ()
2440 Location *location = _editor->find_location_from_marker (_marker, is_start);
2441 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2445 MarkerDrag::motion (GdkEvent* event, bool)
2447 framecnt_t f_delta = 0;
2449 bool move_both = false;
2451 Location *real_location;
2452 Location *copy_location = 0;
2454 framepos_t const newframe = adjusted_current_frame (event);
2456 framepos_t next = newframe;
2458 if (newframe == last_pointer_frame()) {
2462 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2466 MarkerSelection::iterator i;
2467 list<Location*>::iterator x;
2469 /* find the marker we're dragging, and compute the delta */
2471 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2472 x != _copied_locations.end() && i != _editor->selection->markers.end();
2478 if (marker == _marker) {
2480 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2485 if (real_location->is_mark()) {
2486 f_delta = newframe - copy_location->start();
2490 switch (marker->type()) {
2491 case Marker::SessionStart:
2492 case Marker::RangeStart:
2493 case Marker::LoopStart:
2494 case Marker::PunchIn:
2495 f_delta = newframe - copy_location->start();
2498 case Marker::SessionEnd:
2499 case Marker::RangeEnd:
2500 case Marker::LoopEnd:
2501 case Marker::PunchOut:
2502 f_delta = newframe - copy_location->end();
2505 /* what kind of marker is this ? */
2513 if (i == _editor->selection->markers.end()) {
2514 /* hmm, impossible - we didn't find the dragged marker */
2518 /* now move them all */
2520 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2521 x != _copied_locations.end() && i != _editor->selection->markers.end();
2527 /* call this to find out if its the start or end */
2529 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2533 if (real_location->locked()) {
2537 if (copy_location->is_mark()) {
2541 copy_location->set_start (copy_location->start() + f_delta);
2545 framepos_t new_start = copy_location->start() + f_delta;
2546 framepos_t new_end = copy_location->end() + f_delta;
2548 if (is_start) { // start-of-range marker
2551 copy_location->set_start (new_start);
2552 copy_location->set_end (new_end);
2553 } else if (new_start < copy_location->end()) {
2554 copy_location->set_start (new_start);
2555 } else if (newframe > 0) {
2556 _editor->snap_to (next, 1, true);
2557 copy_location->set_end (next);
2558 copy_location->set_start (newframe);
2561 } else { // end marker
2564 copy_location->set_end (new_end);
2565 copy_location->set_start (new_start);
2566 } else if (new_end > copy_location->start()) {
2567 copy_location->set_end (new_end);
2568 } else if (newframe > 0) {
2569 _editor->snap_to (next, -1, true);
2570 copy_location->set_start (next);
2571 copy_location->set_end (newframe);
2576 update_item (copy_location);
2578 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2581 lm->set_position (copy_location->start(), copy_location->end());
2585 assert (!_copied_locations.empty());
2587 _editor->show_verbose_time_cursor (newframe, 10);
2590 _editor->update_canvas_now ();
2595 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2597 if (!movement_occurred) {
2599 /* just a click, do nothing but finish
2600 off the selection process
2603 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2606 case Selection::Set:
2607 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2608 _editor->selection->set (_marker);
2612 case Selection::Toggle:
2613 case Selection::Extend:
2614 case Selection::Add:
2621 _editor->_dragging_edit_point = false;
2623 _editor->begin_reversible_command ( _("move marker") );
2624 XMLNode &before = _editor->session()->locations()->get_state();
2626 MarkerSelection::iterator i;
2627 list<Location*>::iterator x;
2630 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2631 x != _copied_locations.end() && i != _editor->selection->markers.end();
2634 Location * location = _editor->find_location_from_marker (*i, is_start);
2638 if (location->locked()) {
2642 if (location->is_mark()) {
2643 location->set_start ((*x)->start());
2645 location->set ((*x)->start(), (*x)->end());
2650 XMLNode &after = _editor->session()->locations()->get_state();
2651 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2652 _editor->commit_reversible_command ();
2656 MarkerDrag::aborted (bool)
2662 MarkerDrag::update_item (Location* location)
2667 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2669 _cumulative_x_drag (0),
2670 _cumulative_y_drag (0)
2672 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2674 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2680 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2682 Drag::start_grab (event, _editor->cursors()->fader);
2684 // start the grab at the center of the control point so
2685 // the point doesn't 'jump' to the mouse after the first drag
2686 _fixed_grab_x = _point->get_x();
2687 _fixed_grab_y = _point->get_y();
2689 float const fraction = 1 - (_point->get_y() / _point->line().height());
2691 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2693 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2694 event->button.x + 10, event->button.y + 10);
2696 _editor->show_verbose_canvas_cursor ();
2700 ControlPointDrag::motion (GdkEvent* event, bool)
2702 double dx = _drags->current_pointer_x() - last_pointer_x();
2703 double dy = _drags->current_pointer_y() - last_pointer_y();
2705 if (event->button.state & Keyboard::SecondaryModifier) {
2710 /* coordinate in pixels relative to the start of the region (for region-based automation)
2711 or track (for track-based automation) */
2712 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2713 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2715 // calculate zero crossing point. back off by .01 to stay on the
2716 // positive side of zero
2717 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2719 // make sure we hit zero when passing through
2720 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2724 if (_x_constrained) {
2727 if (_y_constrained) {
2731 _cumulative_x_drag = cx - _fixed_grab_x;
2732 _cumulative_y_drag = cy - _fixed_grab_y;
2736 cy = min ((double) _point->line().height(), cy);
2738 framepos_t cx_frames = _editor->unit_to_frame (cx);
2740 if (!_x_constrained) {
2741 _editor->snap_to_with_modifier (cx_frames, event);
2744 cx_frames = min (cx_frames, _point->line().maximum_time());
2746 float const fraction = 1.0 - (cy / _point->line().height());
2748 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2750 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2752 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2756 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2758 if (!movement_occurred) {
2762 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2763 _editor->reset_point_selection ();
2767 motion (event, false);
2770 _point->line().end_drag ();
2771 _editor->session()->commit_reversible_command ();
2775 ControlPointDrag::aborted (bool)
2777 _point->line().reset ();
2781 ControlPointDrag::active (Editing::MouseMode m)
2783 if (m == Editing::MouseGain) {
2784 /* always active in mouse gain */
2788 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2789 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2792 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2795 _cumulative_y_drag (0)
2797 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2801 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2803 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2806 _item = &_line->grab_item ();
2808 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2809 origin, and ditto for y.
2812 double cx = event->button.x;
2813 double cy = event->button.y;
2815 _line->parent_group().w2i (cx, cy);
2817 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2822 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2823 /* no adjacent points */
2827 Drag::start_grab (event, _editor->cursors()->fader);
2829 /* store grab start in parent frame */
2834 double fraction = 1.0 - (cy / _line->height());
2836 _line->start_drag_line (before, after, fraction);
2838 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2839 event->button.x + 10, event->button.y + 10);
2841 _editor->show_verbose_canvas_cursor ();
2845 LineDrag::motion (GdkEvent* event, bool)
2847 double dy = _drags->current_pointer_y() - last_pointer_y();
2849 if (event->button.state & Keyboard::SecondaryModifier) {
2853 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2855 _cumulative_y_drag = cy - _fixed_grab_y;
2858 cy = min ((double) _line->height(), cy);
2860 double const fraction = 1.0 - (cy / _line->height());
2864 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2870 /* we are ignoring x position for this drag, so we can just pass in anything */
2871 _line->drag_motion (0, fraction, true, push);
2873 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2877 LineDrag::finished (GdkEvent* event, bool)
2879 motion (event, false);
2881 _editor->session()->commit_reversible_command ();
2885 LineDrag::aborted (bool)
2890 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2893 _cumulative_x_drag (0)
2895 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2899 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2901 Drag::start_grab (event);
2903 _line = reinterpret_cast<Line*> (_item);
2906 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2908 double cx = event->button.x;
2909 double cy = event->button.y;
2911 _item->property_parent().get_value()->w2i(cx, cy);
2913 /* store grab start in parent frame */
2914 _region_view_grab_x = cx;
2916 _before = *(float*) _item->get_data ("position");
2918 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2920 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2924 FeatureLineDrag::motion (GdkEvent*, bool)
2926 double dx = _drags->current_pointer_x() - last_pointer_x();
2928 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2930 _cumulative_x_drag += dx;
2932 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2941 ArdourCanvas::Points points;
2943 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
2945 _line->get_bounds(x1, y2, x2, y2);
2947 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
2948 points.push_back(Gnome::Art::Point(cx, y2 - y1));
2950 _line->property_points() = points;
2952 float *pos = new float;
2955 _line->set_data ("position", pos);
2961 FeatureLineDrag::finished (GdkEvent*, bool)
2963 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2964 _arv->update_transient(_before, _before);
2968 FeatureLineDrag::aborted (bool)
2973 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2976 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2980 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2982 Drag::start_grab (event);
2983 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2987 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2994 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2996 framepos_t grab = grab_frame ();
2997 if (Config->get_rubberbanding_snaps_to_grid ()) {
2998 _editor->snap_to_with_modifier (grab, event);
3001 /* base start and end on initial click position */
3011 if (_drags->current_pointer_y() < grab_y()) {
3012 y1 = _drags->current_pointer_y();
3015 y2 = _drags->current_pointer_y();
3020 if (start != end || y1 != y2) {
3022 double x1 = _editor->frame_to_pixel (start);
3023 double x2 = _editor->frame_to_pixel (end);
3025 _editor->rubberband_rect->property_x1() = x1;
3026 _editor->rubberband_rect->property_y1() = y1;
3027 _editor->rubberband_rect->property_x2() = x2;
3028 _editor->rubberband_rect->property_y2() = y2;
3030 _editor->rubberband_rect->show();
3031 _editor->rubberband_rect->raise_to_top();
3033 _editor->show_verbose_time_cursor (pf, 10);
3038 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3040 if (movement_occurred) {
3042 motion (event, false);
3045 if (_drags->current_pointer_y() < grab_y()) {
3046 y1 = _drags->current_pointer_y();
3049 y2 = _drags->current_pointer_y();
3054 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3056 _editor->begin_reversible_command (_("rubberband selection"));
3058 if (grab_frame() < last_pointer_frame()) {
3059 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3061 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3064 _editor->commit_reversible_command ();
3067 if (!getenv("ARDOUR_SAE")) {
3068 _editor->selection->clear_tracks();
3070 _editor->selection->clear_regions();
3071 _editor->selection->clear_points ();
3072 _editor->selection->clear_lines ();
3075 _editor->rubberband_rect->hide();
3079 RubberbandSelectDrag::aborted (bool)
3081 _editor->rubberband_rect->hide ();
3084 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3085 : RegionDrag (e, i, p, v)
3087 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3091 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3093 Drag::start_grab (event, cursor);
3095 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3099 TimeFXDrag::motion (GdkEvent* event, bool)
3101 RegionView* rv = _primary;
3103 framepos_t const pf = adjusted_current_frame (event);
3105 if (pf > rv->region()->position()) {
3106 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3109 _editor->show_verbose_time_cursor (pf, 10);
3113 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3115 _primary->get_time_axis_view().hide_timestretch ();
3117 if (!movement_occurred) {
3121 if (last_pointer_frame() < _primary->region()->position()) {
3122 /* backwards drag of the left edge - not usable */
3126 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3128 float percentage = (double) newlen / (double) _primary->region()->length();
3130 #ifndef USE_RUBBERBAND
3131 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3132 if (_primary->region()->data_type() == DataType::AUDIO) {
3133 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3137 _editor->begin_reversible_command (_("timestretch"));
3139 // XXX how do timeFX on multiple regions ?
3144 if (_editor->time_stretch (rs, percentage) == -1) {
3145 error << _("An error occurred while executing time stretch operation") << endmsg;
3150 TimeFXDrag::aborted (bool)
3152 _primary->get_time_axis_view().hide_timestretch ();
3155 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3158 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3162 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3164 Drag::start_grab (event);
3168 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3170 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3174 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3176 if (movement_occurred && _editor->session()) {
3177 /* make sure we stop */
3178 _editor->session()->request_transport_speed (0.0);
3183 ScrubDrag::aborted (bool)
3188 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3192 , _original_pointer_time_axis (-1)
3193 , _last_pointer_time_axis (-1)
3195 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3199 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3201 if (_editor->session() == 0) {
3205 Gdk::Cursor* cursor = 0;
3207 switch (_operation) {
3208 case CreateSelection:
3209 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3214 cursor = _editor->cursors()->selector;
3215 Drag::start_grab (event, cursor);
3218 case SelectionStartTrim:
3219 if (_editor->clicked_axisview) {
3220 _editor->clicked_axisview->order_selection_trims (_item, true);
3222 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3225 case SelectionEndTrim:
3226 if (_editor->clicked_axisview) {
3227 _editor->clicked_axisview->order_selection_trims (_item, false);
3229 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3233 Drag::start_grab (event, cursor);
3237 if (_operation == SelectionMove) {
3238 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3240 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3243 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3247 SelectionDrag::setup_pointer_frame_offset ()
3249 switch (_operation) {
3250 case CreateSelection:
3251 _pointer_frame_offset = 0;
3254 case SelectionStartTrim:
3256 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3259 case SelectionEndTrim:
3260 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3266 SelectionDrag::motion (GdkEvent* event, bool first_move)
3268 framepos_t start = 0;
3272 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3273 if (pending_time_axis.first == 0) {
3277 framepos_t const pending_position = adjusted_current_frame (event);
3279 /* only alter selection if things have changed */
3281 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3285 switch (_operation) {
3286 case CreateSelection:
3288 framepos_t grab = grab_frame ();
3291 _editor->snap_to (grab);
3294 if (pending_position < grab_frame()) {
3295 start = pending_position;
3298 end = pending_position;
3302 /* first drag: Either add to the selection
3303 or create a new selection
3309 /* adding to the selection */
3310 _editor->set_selected_track_as_side_effect (Selection::Add);
3311 //_editor->selection->add (_editor->clicked_axisview);
3312 _editor->clicked_selection = _editor->selection->add (start, end);
3317 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3318 //_editor->selection->set (_editor->clicked_axisview);
3319 _editor->set_selected_track_as_side_effect (Selection::Set);
3322 _editor->clicked_selection = _editor->selection->set (start, end);
3326 /* select the track that we're in */
3327 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3328 // _editor->set_selected_track_as_side_effect (Selection::Add);
3329 _editor->selection->add (pending_time_axis.first);
3330 _added_time_axes.push_back (pending_time_axis.first);
3333 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3334 tracks that we selected in the first place.
3337 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3338 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3340 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3341 while (i != _added_time_axes.end()) {
3343 list<TimeAxisView*>::iterator tmp = i;
3346 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3347 _editor->selection->remove (*i);
3348 _added_time_axes.remove (*i);
3357 case SelectionStartTrim:
3359 start = _editor->selection->time[_editor->clicked_selection].start;
3360 end = _editor->selection->time[_editor->clicked_selection].end;
3362 if (pending_position > end) {
3365 start = pending_position;
3369 case SelectionEndTrim:
3371 start = _editor->selection->time[_editor->clicked_selection].start;
3372 end = _editor->selection->time[_editor->clicked_selection].end;
3374 if (pending_position < start) {
3377 end = pending_position;
3384 start = _editor->selection->time[_editor->clicked_selection].start;
3385 end = _editor->selection->time[_editor->clicked_selection].end;
3387 length = end - start;
3389 start = pending_position;
3390 _editor->snap_to (start);
3392 end = start + length;
3397 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3398 _editor->start_canvas_autoscroll (1, 0);
3402 _editor->selection->replace (_editor->clicked_selection, start, end);
3405 if (_operation == SelectionMove) {
3406 _editor->show_verbose_time_cursor(start, 10);
3408 _editor->show_verbose_time_cursor(pending_position, 10);
3413 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3415 Session* s = _editor->session();
3417 if (movement_occurred) {
3418 motion (event, false);
3419 /* XXX this is not object-oriented programming at all. ick */
3420 if (_editor->selection->time.consolidate()) {
3421 _editor->selection->TimeChanged ();
3424 /* XXX what if its a music time selection? */
3425 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3426 s->request_play_range (&_editor->selection->time, true);
3431 /* just a click, no pointer movement.*/
3433 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3434 _editor->selection->clear_time();
3437 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3438 _editor->selection->set (_editor->clicked_axisview);
3441 if (s && s->get_play_range () && s->transport_rolling()) {
3442 s->request_stop (false, false);
3447 _editor->stop_canvas_autoscroll ();
3451 SelectionDrag::aborted (bool)
3456 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3461 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3463 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3464 physical_screen_height (_editor->get_window()));
3465 _drag_rect->hide ();
3467 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3468 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3472 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3474 if (_editor->session() == 0) {
3478 Gdk::Cursor* cursor = 0;
3480 if (!_editor->temp_location) {
3481 _editor->temp_location = new Location (*_editor->session());
3484 switch (_operation) {
3485 case CreateRangeMarker:
3486 case CreateTransportMarker:
3487 case CreateCDMarker:
3489 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3494 cursor = _editor->cursors()->selector;
3498 Drag::start_grab (event, cursor);
3500 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3504 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3506 framepos_t start = 0;
3508 ArdourCanvas::SimpleRect *crect;
3510 switch (_operation) {
3511 case CreateRangeMarker:
3512 crect = _editor->range_bar_drag_rect;
3514 case CreateTransportMarker:
3515 crect = _editor->transport_bar_drag_rect;
3517 case CreateCDMarker:
3518 crect = _editor->cd_marker_bar_drag_rect;
3521 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3526 framepos_t const pf = adjusted_current_frame (event);
3528 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3529 framepos_t grab = grab_frame ();
3530 _editor->snap_to (grab);
3532 if (pf < grab_frame()) {
3540 /* first drag: Either add to the selection
3541 or create a new selection.
3546 _editor->temp_location->set (start, end);
3550 update_item (_editor->temp_location);
3552 //_drag_rect->raise_to_top();
3557 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3558 _editor->start_canvas_autoscroll (1, 0);
3562 _editor->temp_location->set (start, end);
3564 double x1 = _editor->frame_to_pixel (start);
3565 double x2 = _editor->frame_to_pixel (end);
3566 crect->property_x1() = x1;
3567 crect->property_x2() = x2;
3569 update_item (_editor->temp_location);
3572 _editor->show_verbose_time_cursor (pf, 10);
3577 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3579 Location * newloc = 0;
3583 if (movement_occurred) {
3584 motion (event, false);
3587 switch (_operation) {
3588 case CreateRangeMarker:
3589 case CreateCDMarker:
3591 _editor->begin_reversible_command (_("new range marker"));
3592 XMLNode &before = _editor->session()->locations()->get_state();
3593 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3594 if (_operation == CreateCDMarker) {
3595 flags = Location::IsRangeMarker | Location::IsCDMarker;
3596 _editor->cd_marker_bar_drag_rect->hide();
3599 flags = Location::IsRangeMarker;
3600 _editor->range_bar_drag_rect->hide();
3602 newloc = new Location (
3603 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3606 _editor->session()->locations()->add (newloc, true);
3607 XMLNode &after = _editor->session()->locations()->get_state();
3608 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3609 _editor->commit_reversible_command ();
3613 case CreateTransportMarker:
3614 // popup menu to pick loop or punch
3615 _editor->new_transport_marker_context_menu (&event->button, _item);
3619 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3621 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3626 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3628 if (end == max_framepos) {
3629 end = _editor->session()->current_end_frame ();
3632 if (start == max_framepos) {
3633 start = _editor->session()->current_start_frame ();
3636 switch (_editor->mouse_mode) {
3638 /* find the two markers on either side and then make the selection from it */
3639 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3643 /* find the two markers on either side of the click and make the range out of it */
3644 _editor->selection->set (start, end);
3653 _editor->stop_canvas_autoscroll ();
3657 RangeMarkerBarDrag::aborted (bool)
3663 RangeMarkerBarDrag::update_item (Location* location)
3665 double const x1 = _editor->frame_to_pixel (location->start());
3666 double const x2 = _editor->frame_to_pixel (location->end());
3668 _drag_rect->property_x1() = x1;
3669 _drag_rect->property_x2() = x2;
3672 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3676 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3680 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3682 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3683 Drag::start_grab (event, _editor->cursors()->zoom_out);
3686 Drag::start_grab (event, _editor->cursors()->zoom_in);
3690 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3694 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3699 framepos_t const pf = adjusted_current_frame (event);
3701 framepos_t grab = grab_frame ();
3702 _editor->snap_to_with_modifier (grab, event);
3704 /* base start and end on initial click position */
3716 _editor->zoom_rect->show();
3717 _editor->zoom_rect->raise_to_top();
3720 _editor->reposition_zoom_rect(start, end);
3722 _editor->show_verbose_time_cursor (pf, 10);
3727 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3729 if (movement_occurred) {
3730 motion (event, false);
3732 if (grab_frame() < last_pointer_frame()) {
3733 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3735 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3738 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3739 _editor->tav_zoom_step (_zoom_out);
3741 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3745 _editor->zoom_rect->hide();
3749 MouseZoomDrag::aborted (bool)
3751 _editor->zoom_rect->hide ();
3754 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3756 , _cumulative_dx (0)
3757 , _cumulative_dy (0)
3759 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3761 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3762 _region = &_primary->region_view ();
3763 _note_height = _region->midi_stream_view()->note_height ();
3767 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3769 Drag::start_grab (event);
3771 if (!(_was_selected = _primary->selected())) {
3773 /* tertiary-click means extend selection - we'll do that on button release,
3774 so don't add it here, because otherwise we make it hard to figure
3775 out the "extend-to" range.
3778 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3781 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3784 _region->note_selected (_primary, true);
3786 _region->unique_select (_primary);
3792 /** @return Current total drag x change in frames */
3794 NoteDrag::total_dx () const
3797 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3799 /* primary note time */
3800 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3802 /* new time of the primary note relative to the region position */
3803 frameoffset_t const st = n + dx;
3805 /* snap and return corresponding delta */
3806 return _region->snap_frame_to_frame (st) - n;
3809 /** @return Current total drag y change in notes */
3811 NoteDrag::total_dy () const
3813 /* this is `backwards' to make increasing note number go in the right direction */
3814 double const dy = _drags->current_pointer_y() - grab_y();
3819 if (abs (dy) >= _note_height) {
3821 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3823 ndy = (int8_t) floor (dy / _note_height / 2.0);
3827 /* more positive value = higher pitch and higher y-axis position on track,
3828 which is the inverse of the X-centric geometric universe
3835 NoteDrag::motion (GdkEvent *, bool)
3837 /* Total change in x and y since the start of the drag */
3838 frameoffset_t const dx = total_dx ();
3839 int8_t const dy = -total_dy ();
3841 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3842 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3843 double const tdy = dy * _note_height - _cumulative_dy;
3846 _cumulative_dx += tdx;
3847 _cumulative_dy += tdy;
3849 int8_t note_delta = total_dy();
3851 _region->move_selection (tdx, tdy, note_delta);
3854 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3855 (int) floor (_primary->note()->note() + note_delta));
3857 _editor->show_verbose_canvas_cursor_with (buf);
3862 NoteDrag::finished (GdkEvent* ev, bool moved)
3865 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3867 if (_was_selected) {
3868 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3870 _region->note_deselected (_primary);
3873 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3874 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3876 if (!extend && !add && _region->selection_size() > 1) {
3877 _region->unique_select (_primary);
3878 } else if (extend) {
3879 _region->note_selected (_primary, true, true);
3881 /* it was added during button press */
3886 _region->note_dropped (_primary, total_dx(), total_dy());
3891 NoteDrag::aborted (bool)
3896 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3897 : Drag (editor, item)
3899 , _nothing_to_drag (false)
3901 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3903 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3906 /* get all lines in the automation view */
3907 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3909 /* find those that overlap the ranges being dragged */
3910 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3911 while (i != lines.end ()) {
3912 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3915 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3917 /* check this range against all the AudioRanges that we are using */
3918 list<AudioRange>::const_iterator k = _ranges.begin ();
3919 while (k != _ranges.end()) {
3920 if (k->coverage (r.first, r.second) != OverlapNone) {
3926 /* add it to our list if it overlaps at all */
3927 if (k != _ranges.end()) {
3932 _lines.push_back (n);
3938 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3942 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3944 Drag::start_grab (event, cursor);
3946 /* Get line states before we start changing things */
3947 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3948 i->state = &i->line->get_state ();
3951 if (_ranges.empty()) {
3953 /* No selected time ranges: drag all points */
3954 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3955 uint32_t const N = i->line->npoints ();
3956 for (uint32_t j = 0; j < N; ++j) {
3957 i->points.push_back (i->line->nth (j));
3963 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3965 framecnt_t const half = (i->start + i->end) / 2;
3967 /* find the line that this audio range starts in */
3968 list<Line>::iterator j = _lines.begin();
3969 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3973 if (j != _lines.end()) {
3974 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3976 /* j is the line that this audio range starts in; fade into it;
3977 64 samples length plucked out of thin air.
3980 framepos_t a = i->start + 64;
3985 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3986 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3988 the_list->add (p, the_list->eval (p));
3989 j->line->add_always_in_view (p);
3990 the_list->add (q, the_list->eval (q));
3991 j->line->add_always_in_view (q);
3994 /* same thing for the end */
3997 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4001 if (j != _lines.end()) {
4002 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4004 /* j is the line that this audio range starts in; fade out of it;
4005 64 samples length plucked out of thin air.
4008 framepos_t b = i->end - 64;
4013 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4014 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4016 the_list->add (p, the_list->eval (p));
4017 j->line->add_always_in_view (p);
4018 the_list->add (q, the_list->eval (q));
4019 j->line->add_always_in_view (q);
4023 _nothing_to_drag = true;
4025 /* Find all the points that should be dragged and put them in the relevant
4026 points lists in the Line structs.
4029 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4031 uint32_t const N = i->line->npoints ();
4032 for (uint32_t j = 0; j < N; ++j) {
4034 /* here's a control point on this line */
4035 ControlPoint* p = i->line->nth (j);
4036 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4038 /* see if it's inside a range */
4039 list<AudioRange>::const_iterator k = _ranges.begin ();
4040 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4044 if (k != _ranges.end()) {
4045 /* dragging this point */
4046 _nothing_to_drag = false;
4047 i->points.push_back (p);
4053 if (_nothing_to_drag) {
4057 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4058 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4063 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4065 if (_nothing_to_drag) {
4069 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4070 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4072 /* we are ignoring x position for this drag, so we can just pass in anything */
4073 i->line->drag_motion (0, f, true, false);
4078 AutomationRangeDrag::finished (GdkEvent* event, bool)
4080 if (_nothing_to_drag) {
4084 motion (event, false);
4085 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4086 i->line->end_drag ();
4087 i->line->clear_always_in_view ();
4090 _editor->session()->commit_reversible_command ();
4094 AutomationRangeDrag::aborted (bool)
4096 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4097 i->line->clear_always_in_view ();
4102 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4105 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4106 layer = v->region()->layer ();
4107 initial_y = v->get_canvas_group()->property_y ();
4108 initial_playlist = v->region()->playlist ();
4109 initial_position = v->region()->position ();
4110 initial_end = v->region()->position () + v->region()->length ();
4113 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4117 , _cumulative_dx (0)
4119 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4123 PatchChangeDrag::motion (GdkEvent* ev, bool)
4125 framepos_t f = adjusted_current_frame (ev);
4126 boost::shared_ptr<Region> r = _region_view->region ();
4127 f = max (f, r->position ());
4128 f = min (f, r->last_frame ());
4130 framecnt_t const dxf = f - grab_frame();
4131 double const dxu = _editor->frame_to_unit (dxf);
4132 _patch_change->move (dxu - _cumulative_dx, 0);
4133 _cumulative_dx = dxu;
4137 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4139 if (!movement_occurred) {
4143 boost::shared_ptr<Region> r (_region_view->region ());
4145 framepos_t f = adjusted_current_frame (ev);
4146 f = max (f, r->position ());
4147 f = min (f, r->last_frame ());
4149 _region_view->move_patch_change (
4151 _region_view->frames_to_beats (f - r->position() - r->start())
4156 PatchChangeDrag::aborted (bool)
4158 _patch_change->move (-_cumulative_dx, 0);
4162 PatchChangeDrag::setup_pointer_frame_offset ()
4164 boost::shared_ptr<Region> region = _region_view->region ();
4165 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();