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 () - _raw_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 (in pixels) */
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 ();
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 /** @param b true to brush, otherwise false.
1181 * @param c true to make copies of the regions being moved, otherwise false.
1183 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1184 : RegionMotionDrag (e, i, p, v, b),
1187 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1190 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1191 if (rtv && rtv->is_track()) {
1192 speed = rtv->track()->speed ();
1195 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1199 RegionMoveDrag::setup_pointer_frame_offset ()
1201 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1204 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1205 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1207 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1209 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1210 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1212 _primary = v->view()->create_region_view (r, false, false);
1214 _primary->get_canvas_group()->show ();
1215 _primary->set_position (pos, 0);
1216 _views.push_back (DraggingView (_primary, this));
1218 _last_frame_position = pos;
1220 _item = _primary->get_canvas_group ();
1224 RegionInsertDrag::finished (GdkEvent *, bool)
1226 _editor->update_canvas_now ();
1228 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1230 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1231 _primary->get_canvas_group()->property_y() = 0;
1233 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1235 _editor->begin_reversible_command (Operations::insert_region);
1236 playlist->clear_changes ();
1237 playlist->add_region (_primary->region (), _last_frame_position);
1238 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1239 _editor->commit_reversible_command ();
1247 RegionInsertDrag::aborted (bool)
1254 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1255 : RegionMoveDrag (e, i, p, v, false, false)
1257 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1260 struct RegionSelectionByPosition {
1261 bool operator() (RegionView*a, RegionView* b) {
1262 return a->region()->position () < b->region()->position();
1267 RegionSpliceDrag::motion (GdkEvent* event, bool)
1269 /* Which trackview is this ? */
1271 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1272 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1273 layer_t layer = tvp.second;
1275 if (tv && tv->layer_display() == Overlaid) {
1279 /* The region motion is only processed if the pointer is over
1283 if (!tv || !tv->is_track()) {
1284 /* To make sure we hide the verbose canvas cursor when the mouse is
1285 not held over and audiotrack.
1287 _editor->hide_verbose_canvas_cursor ();
1293 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1299 RegionSelection copy (_editor->selection->regions);
1301 RegionSelectionByPosition cmp;
1304 framepos_t const pf = adjusted_current_frame (event);
1306 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1308 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1314 boost::shared_ptr<Playlist> playlist;
1316 if ((playlist = atv->playlist()) == 0) {
1320 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1325 if (pf < (*i)->region()->last_frame() + 1) {
1329 if (pf > (*i)->region()->first_frame()) {
1335 playlist->shuffle ((*i)->region(), dir);
1340 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1342 RegionMoveDrag::finished (event, movement_occurred);
1346 RegionSpliceDrag::aborted (bool)
1351 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1353 _view (dynamic_cast<MidiTimeAxisView*> (v))
1355 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1361 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1365 _view->playlist()->freeze ();
1368 framepos_t const f = adjusted_current_frame (event);
1369 if (f < grab_frame()) {
1370 _region->set_position (f, this);
1373 /* again, don't use a zero-length region (see above) */
1374 framecnt_t const len = abs (f - grab_frame ());
1375 _region->set_length (len < 1 ? 1 : len, this);
1381 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1383 if (!movement_occurred) {
1386 _view->playlist()->thaw ();
1390 _editor->commit_reversible_command ();
1395 RegionCreateDrag::add_region ()
1397 if (_editor->session()) {
1398 const TempoMap& map (_editor->session()->tempo_map());
1399 framecnt_t pos = grab_frame();
1400 const Meter& m = map.meter_at (pos);
1401 /* not that the frame rate used here can be affected by pull up/down which
1404 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1405 _region = _view->add_region (grab_frame(), len, false);
1410 RegionCreateDrag::aborted (bool)
1413 _view->playlist()->thaw ();
1419 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1423 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1427 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1429 Gdk::Cursor* cursor;
1430 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1431 float x_fraction = cnote->mouse_x_fraction ();
1433 if (x_fraction > 0.0 && x_fraction < 0.25) {
1434 cursor = _editor->cursors()->left_side_trim;
1436 cursor = _editor->cursors()->right_side_trim;
1439 Drag::start_grab (event, cursor);
1441 region = &cnote->region_view();
1443 double const region_start = region->get_position_pixels();
1444 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1446 if (grab_x() <= middle_point) {
1447 cursor = _editor->cursors()->left_side_trim;
1450 cursor = _editor->cursors()->right_side_trim;
1454 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1456 if (event->motion.state & Keyboard::PrimaryModifier) {
1462 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1464 if (ms.size() > 1) {
1465 /* has to be relative, may make no sense otherwise */
1469 /* select this note; if it is already selected, preserve the existing selection,
1470 otherwise make this note the only one selected.
1472 region->note_selected (cnote, cnote->selected ());
1474 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1475 MidiRegionSelection::iterator next;
1478 (*r)->begin_resizing (at_front);
1484 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1486 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1487 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1488 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1493 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1495 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1496 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1497 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1502 NoteResizeDrag::aborted (bool)
1507 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1510 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1514 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1520 RegionGainDrag::finished (GdkEvent *, bool)
1526 RegionGainDrag::aborted (bool)
1531 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1532 : RegionDrag (e, i, p, v)
1534 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1538 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1541 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1542 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1544 if (tv && tv->is_track()) {
1545 speed = tv->track()->speed();
1548 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1549 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1550 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1552 framepos_t const pf = adjusted_current_frame (event);
1554 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1555 /* Move the contents of the region around without changing the region bounds */
1556 _operation = ContentsTrim;
1557 Drag::start_grab (event, _editor->cursors()->trimmer);
1559 /* These will get overridden for a point trim.*/
1560 if (pf < (region_start + region_length/2)) {
1561 /* closer to front */
1562 _operation = StartTrim;
1563 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1566 _operation = EndTrim;
1567 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1571 switch (_operation) {
1573 _editor->show_verbose_time_cursor (region_start, 10);
1574 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1575 i->view->trim_front_starting ();
1579 _editor->show_verbose_time_cursor (region_end, 10);
1582 _editor->show_verbose_time_cursor (pf, 10);
1586 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1587 i->view->region()->suspend_property_changes ();
1592 TrimDrag::motion (GdkEvent* event, bool first_move)
1594 RegionView* rv = _primary;
1597 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1598 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1599 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1601 if (tv && tv->is_track()) {
1602 speed = tv->track()->speed();
1605 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1611 switch (_operation) {
1613 trim_type = "Region start trim";
1616 trim_type = "Region end trim";
1619 trim_type = "Region content trim";
1623 _editor->begin_reversible_command (trim_type);
1625 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1626 RegionView* rv = i->view;
1627 rv->fake_set_opaque (false);
1628 rv->enable_display (false);
1629 rv->region()->playlist()->clear_owned_changes ();
1631 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1634 arv->temporarily_hide_envelope ();
1637 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1638 insert_result = _editor->motion_frozen_playlists.insert (pl);
1640 if (insert_result.second) {
1646 bool non_overlap_trim = false;
1648 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1649 non_overlap_trim = true;
1652 switch (_operation) {
1654 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1655 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1660 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1661 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1667 bool swap_direction = false;
1669 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1670 swap_direction = true;
1673 framecnt_t frame_delta = 0;
1675 bool left_direction = false;
1676 if (last_pointer_frame() > adjusted_current_frame(event)) {
1677 left_direction = true;
1680 if (left_direction) {
1681 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1683 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1686 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1687 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1693 switch (_operation) {
1695 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1698 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1701 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1708 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1710 if (movement_occurred) {
1711 motion (event, false);
1713 /* This must happen before the region's StatefulDiffCommand is created, as it may
1714 `correct' (ahem) the region's _start from being negative to being zero. It
1715 needs to be zero in the undo record.
1717 if (_operation == StartTrim) {
1718 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1719 i->view->trim_front_ending ();
1723 if (!_editor->selection->selected (_primary)) {
1724 _primary->thaw_after_trim ();
1727 set<boost::shared_ptr<Playlist> > diffed_playlists;
1729 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1730 i->view->thaw_after_trim ();
1731 i->view->enable_display (true);
1732 i->view->fake_set_opaque (true);
1734 /* Trimming one region may affect others on the playlist, so we need
1735 to get undo Commands from the whole playlist rather than just the
1736 region. Use diffed_playlists to make sure we don't diff a given
1737 playlist more than once.
1739 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1740 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1741 vector<Command*> cmds;
1743 _editor->session()->add_commands (cmds);
1744 diffed_playlists.insert (p);
1748 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1752 _editor->motion_frozen_playlists.clear ();
1753 _editor->commit_reversible_command();
1756 /* no mouse movement */
1757 _editor->point_trim (event, adjusted_current_frame (event));
1760 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1761 if (_operation == StartTrim) {
1762 i->view->trim_front_ending ();
1765 i->view->region()->resume_property_changes ();
1770 TrimDrag::aborted (bool movement_occurred)
1772 /* Our motion method is changing model state, so use the Undo system
1773 to cancel. Perhaps not ideal, as this will leave an Undo point
1774 behind which may be slightly odd from the user's point of view.
1779 if (movement_occurred) {
1783 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1784 i->view->region()->resume_property_changes ();
1789 TrimDrag::setup_pointer_frame_offset ()
1791 list<DraggingView>::iterator i = _views.begin ();
1792 while (i != _views.end() && i->view != _primary) {
1796 if (i == _views.end()) {
1800 switch (_operation) {
1802 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1805 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1812 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1816 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1818 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1823 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1826 // create a dummy marker for visual representation of moving the copy.
1827 // The actual copying is not done before we reach the finish callback.
1829 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1831 MeterMarker* new_marker = new MeterMarker (
1833 *_editor->meter_group,
1834 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1836 *new MeterSection (_marker->meter())
1839 _item = &new_marker->the_item ();
1840 _marker = new_marker;
1844 MetricSection& section (_marker->meter());
1846 if (!section.movable()) {
1852 Drag::start_grab (event, cursor);
1854 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1858 MeterMarkerDrag::setup_pointer_frame_offset ()
1860 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1864 MeterMarkerDrag::motion (GdkEvent* event, bool)
1866 framepos_t const pf = adjusted_current_frame (event);
1868 _marker->set_position (pf);
1870 _editor->show_verbose_time_cursor (pf, 10);
1874 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1876 if (!movement_occurred) {
1880 motion (event, false);
1882 Timecode::BBT_Time when;
1884 TempoMap& map (_editor->session()->tempo_map());
1885 map.bbt_time (last_pointer_frame(), when);
1887 if (_copy == true) {
1888 _editor->begin_reversible_command (_("copy meter mark"));
1889 XMLNode &before = map.get_state();
1890 map.add_meter (_marker->meter(), when);
1891 XMLNode &after = map.get_state();
1892 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1893 _editor->commit_reversible_command ();
1895 // delete the dummy marker we used for visual representation of copying.
1896 // a new visual marker will show up automatically.
1899 _editor->begin_reversible_command (_("move meter mark"));
1900 XMLNode &before = map.get_state();
1901 map.move_meter (_marker->meter(), when);
1902 XMLNode &after = map.get_state();
1903 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1904 _editor->commit_reversible_command ();
1909 MeterMarkerDrag::aborted (bool)
1911 _marker->set_position (_marker->meter().frame ());
1914 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1918 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1920 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1925 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1929 // create a dummy marker for visual representation of moving the copy.
1930 // The actual copying is not done before we reach the finish callback.
1932 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1934 TempoMarker* new_marker = new TempoMarker (
1936 *_editor->tempo_group,
1937 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1939 *new TempoSection (_marker->tempo())
1942 _item = &new_marker->the_item ();
1943 _marker = new_marker;
1947 Drag::start_grab (event, cursor);
1949 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1953 TempoMarkerDrag::setup_pointer_frame_offset ()
1955 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1959 TempoMarkerDrag::motion (GdkEvent* event, bool)
1961 framepos_t const pf = adjusted_current_frame (event);
1962 _marker->set_position (pf);
1963 _editor->show_verbose_time_cursor (pf, 10);
1967 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1969 if (!movement_occurred) {
1973 motion (event, false);
1975 Timecode::BBT_Time when;
1977 TempoMap& map (_editor->session()->tempo_map());
1978 map.bbt_time (last_pointer_frame(), when);
1980 if (_copy == true) {
1981 _editor->begin_reversible_command (_("copy tempo mark"));
1982 XMLNode &before = map.get_state();
1983 map.add_tempo (_marker->tempo(), when);
1984 XMLNode &after = map.get_state();
1985 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1986 _editor->commit_reversible_command ();
1988 // delete the dummy marker we used for visual representation of copying.
1989 // a new visual marker will show up automatically.
1992 _editor->begin_reversible_command (_("move tempo mark"));
1993 XMLNode &before = map.get_state();
1994 map.move_tempo (_marker->tempo(), when);
1995 XMLNode &after = map.get_state();
1996 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1997 _editor->commit_reversible_command ();
2002 TempoMarkerDrag::aborted (bool)
2004 _marker->set_position (_marker->tempo().frame());
2007 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2011 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2014 /** Do all the things we do when dragging the playhead to make it look as though
2015 * we have located, without actually doing the locate (because that would cause
2016 * the diskstream buffers to be refilled, which is too slow).
2019 CursorDrag::fake_locate (framepos_t t)
2021 _editor->playhead_cursor->set_position (t);
2023 Session* s = _editor->session ();
2024 if (s->timecode_transmission_suspended ()) {
2025 framepos_t const f = _editor->playhead_cursor->current_frame;
2026 s->send_mmc_locate (f);
2027 s->send_full_time_code (f);
2030 _editor->show_verbose_time_cursor (t, 10);
2031 _editor->UpdateAllTransportClocks (t);
2035 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2037 Drag::start_grab (event, c);
2039 framepos_t where = _editor->event_frame (event, 0, 0);
2040 _editor->snap_to_with_modifier (where, event);
2042 _editor->_dragging_playhead = true;
2044 Session* s = _editor->session ();
2047 if (_was_rolling && _stop) {
2051 if (s->is_auditioning()) {
2052 s->cancel_audition ();
2055 s->request_suspend_timecode_transmission ();
2056 while (!s->timecode_transmission_suspended ()) {
2057 /* twiddle our thumbs */
2061 fake_locate (where);
2065 CursorDrag::motion (GdkEvent* event, bool)
2067 framepos_t const adjusted_frame = adjusted_current_frame (event);
2069 if (adjusted_frame == last_pointer_frame()) {
2073 fake_locate (adjusted_frame);
2076 _editor->update_canvas_now ();
2081 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2083 _editor->_dragging_playhead = false;
2085 if (!movement_occurred && _stop) {
2089 motion (event, false);
2091 Session* s = _editor->session ();
2093 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2094 _editor->_pending_locate_request = true;
2095 s->request_resume_timecode_transmission ();
2100 CursorDrag::aborted (bool)
2102 if (_editor->_dragging_playhead) {
2103 _editor->session()->request_resume_timecode_transmission ();
2104 _editor->_dragging_playhead = false;
2107 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2110 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2111 : RegionDrag (e, i, p, v)
2113 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2117 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2119 Drag::start_grab (event, cursor);
2121 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2122 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2124 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2126 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2130 FadeInDrag::setup_pointer_frame_offset ()
2132 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2133 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2134 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2138 FadeInDrag::motion (GdkEvent* event, bool)
2140 framecnt_t fade_length;
2142 framepos_t const pos = adjusted_current_frame (event);
2144 boost::shared_ptr<Region> region = _primary->region ();
2146 if (pos < (region->position() + 64)) {
2147 fade_length = 64; // this should be a minimum defined somewhere
2148 } else if (pos > region->last_frame()) {
2149 fade_length = region->length();
2151 fade_length = pos - region->position();
2154 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2156 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2162 tmp->reset_fade_in_shape_width (fade_length);
2163 tmp->show_fade_line((framecnt_t) fade_length);
2166 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2170 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2172 if (!movement_occurred) {
2176 framecnt_t fade_length;
2178 framepos_t const pos = adjusted_current_frame (event);
2180 boost::shared_ptr<Region> region = _primary->region ();
2182 if (pos < (region->position() + 64)) {
2183 fade_length = 64; // this should be a minimum defined somewhere
2184 } else if (pos > region->last_frame()) {
2185 fade_length = region->length();
2187 fade_length = pos - region->position();
2190 _editor->begin_reversible_command (_("change fade in length"));
2192 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2194 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2200 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2201 XMLNode &before = alist->get_state();
2203 tmp->audio_region()->set_fade_in_length (fade_length);
2204 tmp->audio_region()->set_fade_in_active (true);
2205 tmp->hide_fade_line();
2207 XMLNode &after = alist->get_state();
2208 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2211 _editor->commit_reversible_command ();
2215 FadeInDrag::aborted (bool)
2217 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2218 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2224 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2225 tmp->hide_fade_line();
2229 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2230 : RegionDrag (e, i, p, v)
2232 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2236 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2238 Drag::start_grab (event, cursor);
2240 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2241 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2243 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2245 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2249 FadeOutDrag::setup_pointer_frame_offset ()
2251 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2252 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2253 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2257 FadeOutDrag::motion (GdkEvent* event, bool)
2259 framecnt_t fade_length;
2261 framepos_t const pos = adjusted_current_frame (event);
2263 boost::shared_ptr<Region> region = _primary->region ();
2265 if (pos > (region->last_frame() - 64)) {
2266 fade_length = 64; // this should really be a minimum fade defined somewhere
2268 else if (pos < region->position()) {
2269 fade_length = region->length();
2272 fade_length = region->last_frame() - pos;
2275 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2277 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2283 tmp->reset_fade_out_shape_width (fade_length);
2284 tmp->show_fade_line(region->length() - fade_length);
2287 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2291 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2293 if (!movement_occurred) {
2297 framecnt_t fade_length;
2299 framepos_t const pos = adjusted_current_frame (event);
2301 boost::shared_ptr<Region> region = _primary->region ();
2303 if (pos > (region->last_frame() - 64)) {
2304 fade_length = 64; // this should really be a minimum fade defined somewhere
2306 else if (pos < region->position()) {
2307 fade_length = region->length();
2310 fade_length = region->last_frame() - pos;
2313 _editor->begin_reversible_command (_("change fade out length"));
2315 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2317 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2323 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2324 XMLNode &before = alist->get_state();
2326 tmp->audio_region()->set_fade_out_length (fade_length);
2327 tmp->audio_region()->set_fade_out_active (true);
2328 tmp->hide_fade_line();
2330 XMLNode &after = alist->get_state();
2331 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2334 _editor->commit_reversible_command ();
2338 FadeOutDrag::aborted (bool)
2340 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2341 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2347 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2348 tmp->hide_fade_line();
2352 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2355 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2357 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2360 _points.push_back (Gnome::Art::Point (0, 0));
2361 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2364 MarkerDrag::~MarkerDrag ()
2366 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2372 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2374 Drag::start_grab (event, cursor);
2378 Location *location = _editor->find_location_from_marker (_marker, is_start);
2379 _editor->_dragging_edit_point = true;
2381 update_item (location);
2383 // _drag_line->show();
2384 // _line->raise_to_top();
2387 _editor->show_verbose_time_cursor (location->start(), 10);
2389 _editor->show_verbose_time_cursor (location->end(), 10);
2392 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2395 case Selection::Toggle:
2396 _editor->selection->toggle (_marker);
2398 case Selection::Set:
2399 if (!_editor->selection->selected (_marker)) {
2400 _editor->selection->set (_marker);
2403 case Selection::Extend:
2405 Locations::LocationList ll;
2406 list<Marker*> to_add;
2408 _editor->selection->markers.range (s, e);
2409 s = min (_marker->position(), s);
2410 e = max (_marker->position(), e);
2413 if (e < max_framepos) {
2416 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2417 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2418 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2421 to_add.push_back (lm->start);
2424 to_add.push_back (lm->end);
2428 if (!to_add.empty()) {
2429 _editor->selection->add (to_add);
2433 case Selection::Add:
2434 _editor->selection->add (_marker);
2438 /* Set up copies for us to manipulate during the drag */
2440 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2441 Location* l = _editor->find_location_from_marker (*i, is_start);
2442 _copied_locations.push_back (new Location (*l));
2447 MarkerDrag::setup_pointer_frame_offset ()
2450 Location *location = _editor->find_location_from_marker (_marker, is_start);
2451 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2455 MarkerDrag::motion (GdkEvent* event, bool)
2457 framecnt_t f_delta = 0;
2459 bool move_both = false;
2461 Location *real_location;
2462 Location *copy_location = 0;
2464 framepos_t const newframe = adjusted_current_frame (event);
2466 framepos_t next = newframe;
2468 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2472 MarkerSelection::iterator i;
2473 list<Location*>::iterator x;
2475 /* find the marker we're dragging, and compute the delta */
2477 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2478 x != _copied_locations.end() && i != _editor->selection->markers.end();
2484 if (marker == _marker) {
2486 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2491 if (real_location->is_mark()) {
2492 f_delta = newframe - copy_location->start();
2496 switch (marker->type()) {
2497 case Marker::SessionStart:
2498 case Marker::RangeStart:
2499 case Marker::LoopStart:
2500 case Marker::PunchIn:
2501 f_delta = newframe - copy_location->start();
2504 case Marker::SessionEnd:
2505 case Marker::RangeEnd:
2506 case Marker::LoopEnd:
2507 case Marker::PunchOut:
2508 f_delta = newframe - copy_location->end();
2511 /* what kind of marker is this ? */
2519 if (i == _editor->selection->markers.end()) {
2520 /* hmm, impossible - we didn't find the dragged marker */
2524 /* now move them all */
2526 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2527 x != _copied_locations.end() && i != _editor->selection->markers.end();
2533 /* call this to find out if its the start or end */
2535 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2539 if (real_location->locked()) {
2543 if (copy_location->is_mark()) {
2547 copy_location->set_start (copy_location->start() + f_delta);
2551 framepos_t new_start = copy_location->start() + f_delta;
2552 framepos_t new_end = copy_location->end() + f_delta;
2554 if (is_start) { // start-of-range marker
2557 copy_location->set_start (new_start);
2558 copy_location->set_end (new_end);
2559 } else if (new_start < copy_location->end()) {
2560 copy_location->set_start (new_start);
2561 } else if (newframe > 0) {
2562 _editor->snap_to (next, 1, true);
2563 copy_location->set_end (next);
2564 copy_location->set_start (newframe);
2567 } else { // end marker
2570 copy_location->set_end (new_end);
2571 copy_location->set_start (new_start);
2572 } else if (new_end > copy_location->start()) {
2573 copy_location->set_end (new_end);
2574 } else if (newframe > 0) {
2575 _editor->snap_to (next, -1, true);
2576 copy_location->set_start (next);
2577 copy_location->set_end (newframe);
2582 update_item (copy_location);
2584 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2587 lm->set_position (copy_location->start(), copy_location->end());
2591 assert (!_copied_locations.empty());
2593 _editor->show_verbose_time_cursor (newframe, 10);
2596 _editor->update_canvas_now ();
2601 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2603 if (!movement_occurred) {
2605 /* just a click, do nothing but finish
2606 off the selection process
2609 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2612 case Selection::Set:
2613 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2614 _editor->selection->set (_marker);
2618 case Selection::Toggle:
2619 case Selection::Extend:
2620 case Selection::Add:
2627 _editor->_dragging_edit_point = false;
2629 _editor->begin_reversible_command ( _("move marker") );
2630 XMLNode &before = _editor->session()->locations()->get_state();
2632 MarkerSelection::iterator i;
2633 list<Location*>::iterator x;
2636 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2637 x != _copied_locations.end() && i != _editor->selection->markers.end();
2640 Location * location = _editor->find_location_from_marker (*i, is_start);
2644 if (location->locked()) {
2648 if (location->is_mark()) {
2649 location->set_start ((*x)->start());
2651 location->set ((*x)->start(), (*x)->end());
2656 XMLNode &after = _editor->session()->locations()->get_state();
2657 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2658 _editor->commit_reversible_command ();
2662 MarkerDrag::aborted (bool)
2668 MarkerDrag::update_item (Location* location)
2673 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2675 _cumulative_x_drag (0),
2676 _cumulative_y_drag (0)
2678 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2680 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2686 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2688 Drag::start_grab (event, _editor->cursors()->fader);
2690 // start the grab at the center of the control point so
2691 // the point doesn't 'jump' to the mouse after the first drag
2692 _fixed_grab_x = _point->get_x();
2693 _fixed_grab_y = _point->get_y();
2695 float const fraction = 1 - (_point->get_y() / _point->line().height());
2697 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2699 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2700 event->button.x + 10, event->button.y + 10);
2702 _editor->show_verbose_canvas_cursor ();
2706 ControlPointDrag::motion (GdkEvent* event, bool)
2708 double dx = _drags->current_pointer_x() - last_pointer_x();
2709 double dy = _drags->current_pointer_y() - last_pointer_y();
2711 if (event->button.state & Keyboard::SecondaryModifier) {
2716 /* coordinate in pixels relative to the start of the region (for region-based automation)
2717 or track (for track-based automation) */
2718 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2719 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2721 // calculate zero crossing point. back off by .01 to stay on the
2722 // positive side of zero
2723 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2725 // make sure we hit zero when passing through
2726 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2730 if (_x_constrained) {
2733 if (_y_constrained) {
2737 _cumulative_x_drag = cx - _fixed_grab_x;
2738 _cumulative_y_drag = cy - _fixed_grab_y;
2742 cy = min ((double) _point->line().height(), cy);
2744 framepos_t cx_frames = _editor->unit_to_frame (cx);
2746 if (!_x_constrained) {
2747 _editor->snap_to_with_modifier (cx_frames, event);
2750 cx_frames = min (cx_frames, _point->line().maximum_time());
2752 float const fraction = 1.0 - (cy / _point->line().height());
2754 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2756 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2758 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2762 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2764 if (!movement_occurred) {
2768 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2769 _editor->reset_point_selection ();
2773 motion (event, false);
2776 _point->line().end_drag ();
2777 _editor->session()->commit_reversible_command ();
2781 ControlPointDrag::aborted (bool)
2783 _point->line().reset ();
2787 ControlPointDrag::active (Editing::MouseMode m)
2789 if (m == Editing::MouseGain) {
2790 /* always active in mouse gain */
2794 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2795 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2798 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2801 _cumulative_y_drag (0)
2803 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2807 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2809 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2812 _item = &_line->grab_item ();
2814 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2815 origin, and ditto for y.
2818 double cx = event->button.x;
2819 double cy = event->button.y;
2821 _line->parent_group().w2i (cx, cy);
2823 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2828 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2829 /* no adjacent points */
2833 Drag::start_grab (event, _editor->cursors()->fader);
2835 /* store grab start in parent frame */
2840 double fraction = 1.0 - (cy / _line->height());
2842 _line->start_drag_line (before, after, fraction);
2844 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2845 event->button.x + 10, event->button.y + 10);
2847 _editor->show_verbose_canvas_cursor ();
2851 LineDrag::motion (GdkEvent* event, bool)
2853 double dy = _drags->current_pointer_y() - last_pointer_y();
2855 if (event->button.state & Keyboard::SecondaryModifier) {
2859 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2861 _cumulative_y_drag = cy - _fixed_grab_y;
2864 cy = min ((double) _line->height(), cy);
2866 double const fraction = 1.0 - (cy / _line->height());
2870 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2876 /* we are ignoring x position for this drag, so we can just pass in anything */
2877 _line->drag_motion (0, fraction, true, push);
2879 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2883 LineDrag::finished (GdkEvent* event, bool)
2885 motion (event, false);
2887 _editor->session()->commit_reversible_command ();
2891 LineDrag::aborted (bool)
2896 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2899 _cumulative_x_drag (0)
2901 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2905 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2907 Drag::start_grab (event);
2909 _line = reinterpret_cast<Line*> (_item);
2912 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2914 double cx = event->button.x;
2915 double cy = event->button.y;
2917 _item->property_parent().get_value()->w2i(cx, cy);
2919 /* store grab start in parent frame */
2920 _region_view_grab_x = cx;
2922 _before = *(float*) _item->get_data ("position");
2924 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2926 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2930 FeatureLineDrag::motion (GdkEvent*, bool)
2932 double dx = _drags->current_pointer_x() - last_pointer_x();
2934 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2936 _cumulative_x_drag += dx;
2938 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2947 ArdourCanvas::Points points;
2949 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
2951 _line->get_bounds(x1, y2, x2, y2);
2953 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
2954 points.push_back(Gnome::Art::Point(cx, y2 - y1));
2956 _line->property_points() = points;
2958 float *pos = new float;
2961 _line->set_data ("position", pos);
2967 FeatureLineDrag::finished (GdkEvent*, bool)
2969 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2970 _arv->update_transient(_before, _before);
2974 FeatureLineDrag::aborted (bool)
2979 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2982 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2986 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2988 Drag::start_grab (event);
2989 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2993 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3000 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3002 framepos_t grab = grab_frame ();
3003 if (Config->get_rubberbanding_snaps_to_grid ()) {
3004 _editor->snap_to_with_modifier (grab, event);
3007 /* base start and end on initial click position */
3017 if (_drags->current_pointer_y() < grab_y()) {
3018 y1 = _drags->current_pointer_y();
3021 y2 = _drags->current_pointer_y();
3026 if (start != end || y1 != y2) {
3028 double x1 = _editor->frame_to_pixel (start);
3029 double x2 = _editor->frame_to_pixel (end);
3031 _editor->rubberband_rect->property_x1() = x1;
3032 _editor->rubberband_rect->property_y1() = y1;
3033 _editor->rubberband_rect->property_x2() = x2;
3034 _editor->rubberband_rect->property_y2() = y2;
3036 _editor->rubberband_rect->show();
3037 _editor->rubberband_rect->raise_to_top();
3039 _editor->show_verbose_time_cursor (pf, 10);
3044 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3046 if (movement_occurred) {
3048 motion (event, false);
3051 if (_drags->current_pointer_y() < grab_y()) {
3052 y1 = _drags->current_pointer_y();
3055 y2 = _drags->current_pointer_y();
3060 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3062 _editor->begin_reversible_command (_("rubberband selection"));
3064 if (grab_frame() < last_pointer_frame()) {
3065 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3067 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3070 _editor->commit_reversible_command ();
3073 if (!getenv("ARDOUR_SAE")) {
3074 _editor->selection->clear_tracks();
3076 _editor->selection->clear_regions();
3077 _editor->selection->clear_points ();
3078 _editor->selection->clear_lines ();
3081 _editor->rubberband_rect->hide();
3085 RubberbandSelectDrag::aborted (bool)
3087 _editor->rubberband_rect->hide ();
3090 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3091 : RegionDrag (e, i, p, v)
3093 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3097 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3099 Drag::start_grab (event, cursor);
3101 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3105 TimeFXDrag::motion (GdkEvent* event, bool)
3107 RegionView* rv = _primary;
3109 framepos_t const pf = adjusted_current_frame (event);
3111 if (pf > rv->region()->position()) {
3112 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3115 _editor->show_verbose_time_cursor (pf, 10);
3119 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3121 _primary->get_time_axis_view().hide_timestretch ();
3123 if (!movement_occurred) {
3127 if (last_pointer_frame() < _primary->region()->position()) {
3128 /* backwards drag of the left edge - not usable */
3132 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3134 float percentage = (double) newlen / (double) _primary->region()->length();
3136 #ifndef USE_RUBBERBAND
3137 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3138 if (_primary->region()->data_type() == DataType::AUDIO) {
3139 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3143 _editor->begin_reversible_command (_("timestretch"));
3145 // XXX how do timeFX on multiple regions ?
3150 if (_editor->time_stretch (rs, percentage) == -1) {
3151 error << _("An error occurred while executing time stretch operation") << endmsg;
3156 TimeFXDrag::aborted (bool)
3158 _primary->get_time_axis_view().hide_timestretch ();
3161 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3164 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3168 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3170 Drag::start_grab (event);
3174 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3176 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3180 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3182 if (movement_occurred && _editor->session()) {
3183 /* make sure we stop */
3184 _editor->session()->request_transport_speed (0.0);
3189 ScrubDrag::aborted (bool)
3194 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3198 , _original_pointer_time_axis (-1)
3199 , _last_pointer_time_axis (-1)
3201 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3205 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3207 if (_editor->session() == 0) {
3211 Gdk::Cursor* cursor = 0;
3213 switch (_operation) {
3214 case CreateSelection:
3215 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3220 cursor = _editor->cursors()->selector;
3221 Drag::start_grab (event, cursor);
3224 case SelectionStartTrim:
3225 if (_editor->clicked_axisview) {
3226 _editor->clicked_axisview->order_selection_trims (_item, true);
3228 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3231 case SelectionEndTrim:
3232 if (_editor->clicked_axisview) {
3233 _editor->clicked_axisview->order_selection_trims (_item, false);
3235 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3239 Drag::start_grab (event, cursor);
3243 if (_operation == SelectionMove) {
3244 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3246 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3249 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3253 SelectionDrag::setup_pointer_frame_offset ()
3255 switch (_operation) {
3256 case CreateSelection:
3257 _pointer_frame_offset = 0;
3260 case SelectionStartTrim:
3262 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3265 case SelectionEndTrim:
3266 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3272 SelectionDrag::motion (GdkEvent* event, bool first_move)
3274 framepos_t start = 0;
3278 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3279 if (pending_time_axis.first == 0) {
3283 framepos_t const pending_position = adjusted_current_frame (event);
3285 /* only alter selection if things have changed */
3287 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3291 switch (_operation) {
3292 case CreateSelection:
3294 framepos_t grab = grab_frame ();
3297 _editor->snap_to (grab);
3300 if (pending_position < grab_frame()) {
3301 start = pending_position;
3304 end = pending_position;
3308 /* first drag: Either add to the selection
3309 or create a new selection
3315 /* adding to the selection */
3316 _editor->set_selected_track_as_side_effect (Selection::Add);
3317 //_editor->selection->add (_editor->clicked_axisview);
3318 _editor->clicked_selection = _editor->selection->add (start, end);
3323 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3324 //_editor->selection->set (_editor->clicked_axisview);
3325 _editor->set_selected_track_as_side_effect (Selection::Set);
3328 _editor->clicked_selection = _editor->selection->set (start, end);
3332 /* select the track that we're in */
3333 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3334 // _editor->set_selected_track_as_side_effect (Selection::Add);
3335 _editor->selection->add (pending_time_axis.first);
3336 _added_time_axes.push_back (pending_time_axis.first);
3339 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3340 tracks that we selected in the first place.
3343 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3344 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3346 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3347 while (i != _added_time_axes.end()) {
3349 list<TimeAxisView*>::iterator tmp = i;
3352 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3353 _editor->selection->remove (*i);
3354 _added_time_axes.remove (*i);
3363 case SelectionStartTrim:
3365 start = _editor->selection->time[_editor->clicked_selection].start;
3366 end = _editor->selection->time[_editor->clicked_selection].end;
3368 if (pending_position > end) {
3371 start = pending_position;
3375 case SelectionEndTrim:
3377 start = _editor->selection->time[_editor->clicked_selection].start;
3378 end = _editor->selection->time[_editor->clicked_selection].end;
3380 if (pending_position < start) {
3383 end = pending_position;
3390 start = _editor->selection->time[_editor->clicked_selection].start;
3391 end = _editor->selection->time[_editor->clicked_selection].end;
3393 length = end - start;
3395 start = pending_position;
3396 _editor->snap_to (start);
3398 end = start + length;
3403 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3404 _editor->start_canvas_autoscroll (1, 0);
3408 _editor->selection->replace (_editor->clicked_selection, start, end);
3411 if (_operation == SelectionMove) {
3412 _editor->show_verbose_time_cursor(start, 10);
3414 _editor->show_verbose_time_cursor(pending_position, 10);
3419 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3421 Session* s = _editor->session();
3423 if (movement_occurred) {
3424 motion (event, false);
3425 /* XXX this is not object-oriented programming at all. ick */
3426 if (_editor->selection->time.consolidate()) {
3427 _editor->selection->TimeChanged ();
3430 /* XXX what if its a music time selection? */
3431 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3432 s->request_play_range (&_editor->selection->time, true);
3437 /* just a click, no pointer movement.*/
3439 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3440 _editor->selection->clear_time();
3443 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3444 _editor->selection->set (_editor->clicked_axisview);
3447 if (s && s->get_play_range () && s->transport_rolling()) {
3448 s->request_stop (false, false);
3453 _editor->stop_canvas_autoscroll ();
3457 SelectionDrag::aborted (bool)
3462 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3467 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3469 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3470 physical_screen_height (_editor->get_window()));
3471 _drag_rect->hide ();
3473 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3474 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3478 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3480 if (_editor->session() == 0) {
3484 Gdk::Cursor* cursor = 0;
3486 if (!_editor->temp_location) {
3487 _editor->temp_location = new Location (*_editor->session());
3490 switch (_operation) {
3491 case CreateRangeMarker:
3492 case CreateTransportMarker:
3493 case CreateCDMarker:
3495 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3500 cursor = _editor->cursors()->selector;
3504 Drag::start_grab (event, cursor);
3506 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3510 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3512 framepos_t start = 0;
3514 ArdourCanvas::SimpleRect *crect;
3516 switch (_operation) {
3517 case CreateRangeMarker:
3518 crect = _editor->range_bar_drag_rect;
3520 case CreateTransportMarker:
3521 crect = _editor->transport_bar_drag_rect;
3523 case CreateCDMarker:
3524 crect = _editor->cd_marker_bar_drag_rect;
3527 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3532 framepos_t const pf = adjusted_current_frame (event);
3534 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3535 framepos_t grab = grab_frame ();
3536 _editor->snap_to (grab);
3538 if (pf < grab_frame()) {
3546 /* first drag: Either add to the selection
3547 or create a new selection.
3552 _editor->temp_location->set (start, end);
3556 update_item (_editor->temp_location);
3558 //_drag_rect->raise_to_top();
3563 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3564 _editor->start_canvas_autoscroll (1, 0);
3568 _editor->temp_location->set (start, end);
3570 double x1 = _editor->frame_to_pixel (start);
3571 double x2 = _editor->frame_to_pixel (end);
3572 crect->property_x1() = x1;
3573 crect->property_x2() = x2;
3575 update_item (_editor->temp_location);
3578 _editor->show_verbose_time_cursor (pf, 10);
3583 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3585 Location * newloc = 0;
3589 if (movement_occurred) {
3590 motion (event, false);
3593 switch (_operation) {
3594 case CreateRangeMarker:
3595 case CreateCDMarker:
3597 _editor->begin_reversible_command (_("new range marker"));
3598 XMLNode &before = _editor->session()->locations()->get_state();
3599 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3600 if (_operation == CreateCDMarker) {
3601 flags = Location::IsRangeMarker | Location::IsCDMarker;
3602 _editor->cd_marker_bar_drag_rect->hide();
3605 flags = Location::IsRangeMarker;
3606 _editor->range_bar_drag_rect->hide();
3608 newloc = new Location (
3609 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3612 _editor->session()->locations()->add (newloc, true);
3613 XMLNode &after = _editor->session()->locations()->get_state();
3614 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3615 _editor->commit_reversible_command ();
3619 case CreateTransportMarker:
3620 // popup menu to pick loop or punch
3621 _editor->new_transport_marker_context_menu (&event->button, _item);
3625 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3627 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3632 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3634 if (end == max_framepos) {
3635 end = _editor->session()->current_end_frame ();
3638 if (start == max_framepos) {
3639 start = _editor->session()->current_start_frame ();
3642 switch (_editor->mouse_mode) {
3644 /* find the two markers on either side and then make the selection from it */
3645 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3649 /* find the two markers on either side of the click and make the range out of it */
3650 _editor->selection->set (start, end);
3659 _editor->stop_canvas_autoscroll ();
3663 RangeMarkerBarDrag::aborted (bool)
3669 RangeMarkerBarDrag::update_item (Location* location)
3671 double const x1 = _editor->frame_to_pixel (location->start());
3672 double const x2 = _editor->frame_to_pixel (location->end());
3674 _drag_rect->property_x1() = x1;
3675 _drag_rect->property_x2() = x2;
3678 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3682 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3686 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3688 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3689 Drag::start_grab (event, _editor->cursors()->zoom_out);
3692 Drag::start_grab (event, _editor->cursors()->zoom_in);
3696 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3700 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3705 framepos_t const pf = adjusted_current_frame (event);
3707 framepos_t grab = grab_frame ();
3708 _editor->snap_to_with_modifier (grab, event);
3710 /* base start and end on initial click position */
3722 _editor->zoom_rect->show();
3723 _editor->zoom_rect->raise_to_top();
3726 _editor->reposition_zoom_rect(start, end);
3728 _editor->show_verbose_time_cursor (pf, 10);
3733 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3735 if (movement_occurred) {
3736 motion (event, false);
3738 if (grab_frame() < last_pointer_frame()) {
3739 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3741 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3744 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3745 _editor->tav_zoom_step (_zoom_out);
3747 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3751 _editor->zoom_rect->hide();
3755 MouseZoomDrag::aborted (bool)
3757 _editor->zoom_rect->hide ();
3760 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3762 , _cumulative_dx (0)
3763 , _cumulative_dy (0)
3765 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3767 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3768 _region = &_primary->region_view ();
3769 _note_height = _region->midi_stream_view()->note_height ();
3773 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3775 Drag::start_grab (event);
3777 if (!(_was_selected = _primary->selected())) {
3779 /* tertiary-click means extend selection - we'll do that on button release,
3780 so don't add it here, because otherwise we make it hard to figure
3781 out the "extend-to" range.
3784 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3787 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3790 _region->note_selected (_primary, true);
3792 _region->unique_select (_primary);
3798 /** @return Current total drag x change in frames */
3800 NoteDrag::total_dx () const
3803 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3805 /* primary note time */
3806 frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3808 /* new time of the primary note relative to the region position */
3809 frameoffset_t const st = n + dx;
3811 /* snap and return corresponding delta */
3812 return _region->snap_frame_to_frame (st) - n;
3815 /** @return Current total drag y change in notes */
3817 NoteDrag::total_dy () const
3819 /* this is `backwards' to make increasing note number go in the right direction */
3820 double const dy = _drags->current_pointer_y() - grab_y();
3825 if (abs (dy) >= _note_height) {
3827 ndy = (int8_t) ceil (dy / _note_height / 2.0);
3829 ndy = (int8_t) floor (dy / _note_height / 2.0);
3833 /* more positive value = higher pitch and higher y-axis position on track,
3834 which is the inverse of the X-centric geometric universe
3841 NoteDrag::motion (GdkEvent *, bool)
3843 /* Total change in x and y since the start of the drag */
3844 frameoffset_t const dx = total_dx ();
3845 int8_t const dy = -total_dy ();
3847 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3848 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3849 double const tdy = dy * _note_height - _cumulative_dy;
3852 _cumulative_dx += tdx;
3853 _cumulative_dy += tdy;
3855 int8_t note_delta = total_dy();
3857 _region->move_selection (tdx, tdy, note_delta);
3860 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3861 (int) floor (_primary->note()->note() + note_delta));
3863 _editor->show_verbose_canvas_cursor_with (buf);
3868 NoteDrag::finished (GdkEvent* ev, bool moved)
3871 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3873 if (_was_selected) {
3874 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3876 _region->note_deselected (_primary);
3879 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3880 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3882 if (!extend && !add && _region->selection_size() > 1) {
3883 _region->unique_select (_primary);
3884 } else if (extend) {
3885 _region->note_selected (_primary, true, true);
3887 /* it was added during button press */
3892 _region->note_dropped (_primary, total_dx(), total_dy());
3897 NoteDrag::aborted (bool)
3902 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3903 : Drag (editor, item)
3905 , _nothing_to_drag (false)
3907 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3909 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3912 /* get all lines in the automation view */
3913 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3915 /* find those that overlap the ranges being dragged */
3916 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3917 while (i != lines.end ()) {
3918 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3921 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3923 /* check this range against all the AudioRanges that we are using */
3924 list<AudioRange>::const_iterator k = _ranges.begin ();
3925 while (k != _ranges.end()) {
3926 if (k->coverage (r.first, r.second) != OverlapNone) {
3932 /* add it to our list if it overlaps at all */
3933 if (k != _ranges.end()) {
3938 _lines.push_back (n);
3944 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3948 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3950 Drag::start_grab (event, cursor);
3952 /* Get line states before we start changing things */
3953 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3954 i->state = &i->line->get_state ();
3957 if (_ranges.empty()) {
3959 /* No selected time ranges: drag all points */
3960 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3961 uint32_t const N = i->line->npoints ();
3962 for (uint32_t j = 0; j < N; ++j) {
3963 i->points.push_back (i->line->nth (j));
3969 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3971 framecnt_t const half = (i->start + i->end) / 2;
3973 /* find the line that this audio range starts in */
3974 list<Line>::iterator j = _lines.begin();
3975 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3979 if (j != _lines.end()) {
3980 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3982 /* j is the line that this audio range starts in; fade into it;
3983 64 samples length plucked out of thin air.
3986 framepos_t a = i->start + 64;
3991 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3992 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3994 the_list->add (p, the_list->eval (p));
3995 j->line->add_always_in_view (p);
3996 the_list->add (q, the_list->eval (q));
3997 j->line->add_always_in_view (q);
4000 /* same thing for the end */
4003 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4007 if (j != _lines.end()) {
4008 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4010 /* j is the line that this audio range starts in; fade out of it;
4011 64 samples length plucked out of thin air.
4014 framepos_t b = i->end - 64;
4019 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4020 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4022 the_list->add (p, the_list->eval (p));
4023 j->line->add_always_in_view (p);
4024 the_list->add (q, the_list->eval (q));
4025 j->line->add_always_in_view (q);
4029 _nothing_to_drag = true;
4031 /* Find all the points that should be dragged and put them in the relevant
4032 points lists in the Line structs.
4035 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4037 uint32_t const N = i->line->npoints ();
4038 for (uint32_t j = 0; j < N; ++j) {
4040 /* here's a control point on this line */
4041 ControlPoint* p = i->line->nth (j);
4042 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4044 /* see if it's inside a range */
4045 list<AudioRange>::const_iterator k = _ranges.begin ();
4046 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4050 if (k != _ranges.end()) {
4051 /* dragging this point */
4052 _nothing_to_drag = false;
4053 i->points.push_back (p);
4059 if (_nothing_to_drag) {
4063 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4064 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4069 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4071 if (_nothing_to_drag) {
4075 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4076 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4078 /* we are ignoring x position for this drag, so we can just pass in anything */
4079 i->line->drag_motion (0, f, true, false);
4084 AutomationRangeDrag::finished (GdkEvent* event, bool)
4086 if (_nothing_to_drag) {
4090 motion (event, false);
4091 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4092 i->line->end_drag ();
4093 i->line->clear_always_in_view ();
4096 _editor->session()->commit_reversible_command ();
4100 AutomationRangeDrag::aborted (bool)
4102 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4103 i->line->clear_always_in_view ();
4108 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4111 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4112 layer = v->region()->layer ();
4113 initial_y = v->get_canvas_group()->property_y ();
4114 initial_playlist = v->region()->playlist ();
4115 initial_position = v->region()->position ();
4116 initial_end = v->region()->position () + v->region()->length ();
4119 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4123 , _cumulative_dx (0)
4125 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4129 PatchChangeDrag::motion (GdkEvent* ev, bool)
4131 framepos_t f = adjusted_current_frame (ev);
4132 boost::shared_ptr<Region> r = _region_view->region ();
4133 f = max (f, r->position ());
4134 f = min (f, r->last_frame ());
4136 framecnt_t const dxf = f - grab_frame();
4137 double const dxu = _editor->frame_to_unit (dxf);
4138 _patch_change->move (dxu - _cumulative_dx, 0);
4139 _cumulative_dx = dxu;
4143 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4145 if (!movement_occurred) {
4149 boost::shared_ptr<Region> r (_region_view->region ());
4151 framepos_t f = adjusted_current_frame (ev);
4152 f = max (f, r->position ());
4153 f = min (f, r->last_frame ());
4155 _region_view->move_patch_change (
4157 _region_view->frames_to_beats (f - r->position() - r->start())
4162 PatchChangeDrag::aborted (bool)
4164 _patch_change->move (-_cumulative_dx, 0);
4168 PatchChangeDrag::setup_pointer_frame_offset ()
4170 boost::shared_ptr<Region> region = _region_view->region ();
4171 _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();