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/audioregion.h"
33 #include "ardour/dB.h"
34 #include "ardour/midi_region.h"
35 #include "ardour/operations.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/session.h"
42 #include "audio_region_view.h"
43 #include "midi_region_view.h"
44 #include "ardour_ui.h"
45 #include "gui_thread.h"
46 #include "control_point.h"
48 #include "region_gain_line.h"
49 #include "editor_drag.h"
50 #include "audio_time_axis.h"
51 #include "midi_time_axis.h"
52 #include "canvas-note.h"
53 #include "selection.h"
54 #include "midi_selection.h"
55 #include "automation_time_axis.h"
57 #include "editor_cursors.h"
58 #include "mouse_cursors.h"
59 #include "verbose_cursor.h"
62 using namespace ARDOUR;
65 using namespace Gtkmm2ext;
66 using namespace Editing;
67 using namespace ArdourCanvas;
69 using Gtkmm2ext::Keyboard;
71 double ControlPointDrag::_zero_gain_fraction = -1.0;
73 DragManager::DragManager (Editor* e)
76 , _current_pointer_frame (0)
80 DragManager::~DragManager ()
85 /** Call abort for each active drag */
91 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
96 if (!_drags.empty ()) {
97 _editor->set_follow_playhead (_old_follow_playhead, false);
106 DragManager::add (Drag* d)
108 d->set_manager (this);
109 _drags.push_back (d);
113 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
115 d->set_manager (this);
116 _drags.push_back (d);
121 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
123 /* Prevent follow playhead during the drag to be nice to the user */
124 _old_follow_playhead = _editor->follow_playhead ();
125 _editor->set_follow_playhead (false);
127 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
129 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
130 (*i)->start_grab (e, c);
134 /** Call end_grab for each active drag.
135 * @return true if any drag reported movement having occurred.
138 DragManager::end_grab (GdkEvent* e)
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->end_grab (e);
155 _editor->set_follow_playhead (_old_follow_playhead, false);
161 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
165 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
167 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
168 bool const t = (*i)->motion_handler (e, from_autoscroll);
179 DragManager::have_item (ArdourCanvas::Item* i) const
181 list<Drag*>::const_iterator j = _drags.begin ();
182 while (j != _drags.end() && (*j)->item () != i) {
186 return j != _drags.end ();
189 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
192 , _pointer_frame_offset (0)
193 , _move_threshold_passed (false)
194 , _raw_grab_frame (0)
196 , _last_pointer_frame (0)
202 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
208 cursor = _editor->which_grabber_cursor ();
211 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
215 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
218 cursor = _editor->which_grabber_cursor ();
221 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
223 if (Keyboard::is_button2_event (&event->button)) {
224 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
225 _y_constrained = true;
226 _x_constrained = false;
228 _y_constrained = false;
229 _x_constrained = true;
232 _x_constrained = false;
233 _y_constrained = false;
236 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
237 setup_pointer_frame_offset ();
238 _grab_frame = adjusted_frame (_raw_grab_frame, event);
239 _last_pointer_frame = _grab_frame;
240 _last_pointer_x = _grab_x;
241 _last_pointer_y = _grab_y;
243 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
247 if (_editor->session() && _editor->session()->transport_rolling()) {
250 _was_rolling = false;
253 switch (_editor->snap_type()) {
254 case SnapToRegionStart:
255 case SnapToRegionEnd:
256 case SnapToRegionSync:
257 case SnapToRegionBoundary:
258 _editor->build_region_boundary_cache ();
265 /** Call to end a drag `successfully'. Ungrabs item and calls
266 * subclass' finished() method.
268 * @param event GDK event, or 0.
269 * @return true if some movement occurred, otherwise false.
272 Drag::end_grab (GdkEvent* event)
274 _editor->stop_canvas_autoscroll ();
276 _item->ungrab (event ? event->button.time : 0);
278 finished (event, _move_threshold_passed);
280 _editor->verbose_cursor()->hide ();
282 return _move_threshold_passed;
286 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
290 if (f > _pointer_frame_offset) {
291 pos = f - _pointer_frame_offset;
295 _editor->snap_to_with_modifier (pos, event);
302 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
304 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
308 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
310 /* check to see if we have moved in any way that matters since the last motion event */
311 if (_move_threshold_passed &&
312 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
313 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
317 pair<framecnt_t, int> const threshold = move_threshold ();
319 bool const old_move_threshold_passed = _move_threshold_passed;
321 if (!from_autoscroll && !_move_threshold_passed) {
323 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
324 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
326 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
329 if (active (_editor->mouse_mode) && _move_threshold_passed) {
331 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
332 if (!from_autoscroll) {
333 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
336 motion (event, _move_threshold_passed != old_move_threshold_passed);
338 _last_pointer_x = _drags->current_pointer_x ();
339 _last_pointer_y = _drags->current_pointer_y ();
340 _last_pointer_frame = adjusted_current_frame (event);
348 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
356 aborted (_move_threshold_passed);
358 _editor->stop_canvas_autoscroll ();
359 _editor->verbose_cursor()->hide ();
363 Drag::show_verbose_cursor_time (framepos_t frame)
365 _editor->verbose_cursor()->set_time (
367 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
368 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
371 _editor->verbose_cursor()->show ();
375 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
377 _editor->verbose_cursor()->show (xoffset);
379 _editor->verbose_cursor()->set_duration (
381 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
382 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
387 Drag::show_verbose_cursor_text (string const & text)
389 _editor->verbose_cursor()->show ();
391 _editor->verbose_cursor()->set (
393 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
394 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
399 struct EditorOrderTimeAxisViewSorter {
400 bool operator() (TimeAxisView* a, TimeAxisView* b) {
401 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
402 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
404 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
408 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
412 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
414 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
415 as some of the regions we are dragging may be on such tracks.
418 TrackViewList track_views = _editor->track_views;
419 track_views.sort (EditorOrderTimeAxisViewSorter ());
421 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
422 _time_axis_views.push_back (*i);
424 TimeAxisView::Children children_list = (*i)->get_child_list ();
425 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
426 _time_axis_views.push_back (j->get());
430 /* the list of views can be empty at this point if this is a region list-insert drag
433 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
434 _views.push_back (DraggingView (*i, this));
437 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
441 RegionDrag::region_going_away (RegionView* v)
443 list<DraggingView>::iterator i = _views.begin ();
444 while (i != _views.end() && i->view != v) {
448 if (i != _views.end()) {
453 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
454 * or -1 if it is not found.
457 RegionDrag::find_time_axis_view (TimeAxisView* t) const
460 int const N = _time_axis_views.size ();
461 while (i < N && _time_axis_views[i] != t) {
472 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
473 : RegionDrag (e, i, p, v),
482 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
484 Drag::start_grab (event, cursor);
486 show_verbose_cursor_time (_last_frame_position);
488 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
489 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
490 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
494 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
496 /* compute the amount of pointer motion in frames, and where
497 the region would be if we moved it by that much.
499 *pending_region_position = adjusted_current_frame (event);
501 framepos_t sync_frame;
502 framecnt_t sync_offset;
505 sync_offset = _primary->region()->sync_offset (sync_dir);
507 /* we don't handle a sync point that lies before zero.
509 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
511 sync_frame = *pending_region_position + (sync_dir*sync_offset);
513 _editor->snap_to_with_modifier (sync_frame, event);
515 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
518 *pending_region_position = _last_frame_position;
521 if (*pending_region_position > max_framepos - _primary->region()->length()) {
522 *pending_region_position = _last_frame_position;
527 /* in locked edit mode, reverse the usual meaning of _x_constrained */
528 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
530 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
532 /* x movement since last time (in pixels) */
533 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
535 /* total x movement */
536 framecnt_t total_dx = *pending_region_position;
537 if (regions_came_from_canvas()) {
538 total_dx = total_dx - grab_frame ();
541 /* check that no regions have gone off the start of the session */
542 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
543 if ((i->view->region()->position() + total_dx) < 0) {
545 *pending_region_position = _last_frame_position;
550 _last_frame_position = *pending_region_position;
557 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
559 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
560 int const n = i->time_axis_view + delta_track;
561 if (n < 0 || n >= int (_time_axis_views.size())) {
562 /* off the top or bottom track */
566 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
567 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
568 /* not a track, or the wrong type */
572 int const l = i->layer + delta_layer;
573 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
574 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
575 If it has, the layers will be munged later anyway, so it's ok.
581 /* all regions being dragged are ok with this change */
586 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
588 assert (!_views.empty ());
590 /* Find the TimeAxisView that the pointer is now over */
591 pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
593 /* Bail early if we're not over a track */
594 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
595 if (!rtv || !rtv->is_track()) {
596 _editor->verbose_cursor()->hide ();
600 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
602 /* Here's the current pointer position in terms of time axis view and layer */
603 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
604 layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
606 /* Work out the change in x */
607 framepos_t pending_region_position;
608 double const x_delta = compute_x_delta (event, &pending_region_position);
610 /* Work out the change in y */
611 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
612 int delta_layer = current_pointer_layer - _last_pointer_layer;
614 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
615 /* this y movement is not allowed, so do no y movement this time */
616 delta_time_axis_view = 0;
620 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
621 /* haven't reached next snap point, and we're not switching
622 trackviews nor layers. nothing to do.
627 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
629 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
631 RegionView* rv = i->view;
633 if (rv->region()->locked()) {
639 rv->get_time_axis_view().hide_dependent_views (*rv);
641 /* Reparent to a non scrolling group so that we can keep the
642 region selection above all time axis views.
643 Reparenting means that we will have to move the region view
644 later, as the two parent groups have different coordinates.
647 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
649 rv->fake_set_opaque (true);
651 if (!rv->get_time_axis_view().hidden()) {
652 /* the track that this region view is on is hidden, so hide the region too */
653 rv->get_canvas_group()->hide ();
657 /* If we have moved tracks, we'll fudge the layer delta so that the
658 region gets moved back onto layer 0 on its new track; this avoids
659 confusion when dragging regions from non-zero layers onto different
662 int this_delta_layer = delta_layer;
663 if (delta_time_axis_view != 0) {
664 this_delta_layer = - i->layer;
667 /* The TimeAxisView that this region is now on */
668 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
671 rv->set_height (tv->view()->child_height ());
673 /* Update show/hidden status as the region view may have come from a hidden track,
674 or have moved to one.
677 rv->get_canvas_group()->hide ();
679 rv->get_canvas_group()->show ();
682 /* Update the DraggingView */
683 i->time_axis_view += delta_time_axis_view;
684 i->layer += this_delta_layer;
687 _editor->mouse_brush_insert_region (rv, pending_region_position);
692 /* Get the y coordinate of the top of the track that this region is now on */
693 tv->canvas_display()->i2w (x, y);
694 y += _editor->get_trackview_group_vertical_offset();
696 /* And adjust for the layer that it should be on */
697 StreamView* cv = tv->view ();
698 if (cv->layer_display() == Stacked) {
699 y += (cv->layers() - i->layer - 1) * cv->child_height ();
702 /* Now move the region view */
703 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
706 } /* foreach region */
708 _total_x_delta += x_delta;
711 _editor->cursor_group->raise_to_top();
714 if (x_delta != 0 && !_brushing) {
715 show_verbose_cursor_time (_last_frame_position);
718 _last_pointer_time_axis_view += delta_time_axis_view;
719 _last_pointer_layer += delta_layer;
723 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
725 if (_copy && first_move) {
727 /* duplicate the regionview(s) and region(s) */
729 list<DraggingView> new_regionviews;
731 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
733 RegionView* rv = i->view;
734 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
735 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
737 const boost::shared_ptr<const Region> original = rv->region();
738 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
739 region_copy->set_position (original->position());
743 boost::shared_ptr<AudioRegion> audioregion_copy
744 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
746 nrv = new AudioRegionView (*arv, audioregion_copy);
748 boost::shared_ptr<MidiRegion> midiregion_copy
749 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
750 nrv = new MidiRegionView (*mrv, midiregion_copy);
755 nrv->get_canvas_group()->show ();
756 new_regionviews.push_back (DraggingView (nrv, this));
758 /* swap _primary to the copy */
760 if (rv == _primary) {
764 /* ..and deselect the one we copied */
766 rv->set_selected (false);
769 if (!new_regionviews.empty()) {
771 /* reflect the fact that we are dragging the copies */
773 _views = new_regionviews;
775 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
778 sync the canvas to what we think is its current state
779 without it, the canvas seems to
780 "forget" to update properly after the upcoming reparent()
781 ..only if the mouse is in rapid motion at the time of the grab.
782 something to do with regionview creation taking so long?
784 _editor->update_canvas_now();
788 RegionMotionDrag::motion (event, first_move);
792 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
794 if (!movement_occurred) {
799 /* reverse this here so that we have the correct logic to finalize
803 if (Config->get_edit_mode() == Lock) {
804 _x_constrained = !_x_constrained;
807 assert (!_views.empty ());
809 /* We might have hidden region views so that they weren't visible during the drag
810 (when they have been reparented). Now everything can be shown again, as region
811 views are back in their track parent groups.
813 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
814 i->view->get_canvas_group()->show ();
817 bool const changed_position = (_last_frame_position != _primary->region()->position());
818 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
819 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
821 _editor->update_canvas_now ();
843 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
845 RegionSelection new_views;
846 PlaylistSet modified_playlists;
847 list<RegionView*> views_to_delete;
850 /* all changes were made during motion event handlers */
852 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
856 _editor->commit_reversible_command ();
860 if (_x_constrained) {
861 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
863 _editor->begin_reversible_command (Operations::region_copy);
866 /* insert the regions into their new playlists */
867 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
869 if (i->view->region()->locked()) {
875 if (changed_position && !_x_constrained) {
876 where = i->view->region()->position() - drag_delta;
878 where = i->view->region()->position();
881 RegionView* new_view = insert_region_into_playlist (
882 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
889 new_views.push_back (new_view);
891 /* we don't need the copied RegionView any more */
892 views_to_delete.push_back (i->view);
895 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
896 because when views are deleted they are automagically removed from _views, which messes
899 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
903 /* If we've created new regions either by copying or moving
904 to a new track, we want to replace the old selection with the new ones
907 if (new_views.size() > 0) {
908 _editor->selection->set (new_views);
911 /* write commands for the accumulated diffs for all our modified playlists */
912 add_stateful_diff_commands_for_playlists (modified_playlists);
914 _editor->commit_reversible_command ();
918 RegionMoveDrag::finished_no_copy (
919 bool const changed_position,
920 bool const changed_tracks,
921 framecnt_t const drag_delta
924 RegionSelection new_views;
925 PlaylistSet modified_playlists;
926 PlaylistSet frozen_playlists;
929 /* all changes were made during motion event handlers */
930 _editor->commit_reversible_command ();
934 if (_x_constrained) {
935 _editor->begin_reversible_command (_("fixed time region drag"));
937 _editor->begin_reversible_command (Operations::region_drag);
940 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
942 RegionView* rv = i->view;
944 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
945 layer_t const dest_layer = i->layer;
947 if (rv->region()->locked()) {
954 if (changed_position && !_x_constrained) {
955 where = rv->region()->position() - drag_delta;
957 where = rv->region()->position();
960 if (changed_tracks) {
962 /* insert into new playlist */
964 RegionView* new_view = insert_region_into_playlist (
965 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
973 new_views.push_back (new_view);
975 /* remove from old playlist */
977 /* the region that used to be in the old playlist is not
978 moved to the new one - we use a copy of it. as a result,
979 any existing editor for the region should no longer be
982 rv->hide_region_editor();
983 rv->fake_set_opaque (false);
985 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
989 rv->region()->clear_changes ();
992 motion on the same track. plonk the previously reparented region
993 back to its original canvas group (its streamview).
994 No need to do anything for copies as they are fake regions which will be deleted.
997 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
998 rv->get_canvas_group()->property_y() = i->initial_y;
999 rv->get_time_axis_view().reveal_dependent_views (*rv);
1001 /* just change the model */
1003 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1005 if (dest_rtv->view()->layer_display() == Stacked) {
1006 rv->region()->set_layer (dest_layer);
1007 rv->region()->set_pending_explicit_relayer (true);
1010 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1012 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1015 playlist->freeze ();
1018 /* this movement may result in a crossfade being modified, so we need to get undo
1019 data from the playlist as well as the region.
1022 r = modified_playlists.insert (playlist);
1024 playlist->clear_changes ();
1027 rv->region()->set_position (where);
1029 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1032 if (changed_tracks) {
1034 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1035 was selected in all of them, then removing it from a playlist will have removed all
1036 trace of it from _views (i.e. there were N regions selected, we removed 1,
1037 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1038 corresponding regionview, and _views is now empty).
1040 This could have invalidated any and all iterators into _views.
1042 The heuristic we use here is: if the region selection is empty, break out of the loop
1043 here. if the region selection is not empty, then restart the loop because we know that
1044 we must have removed at least the region(view) we've just been working on as well as any
1045 that we processed on previous iterations.
1047 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1048 we can just iterate.
1052 if (_views.empty()) {
1063 /* If we've created new regions either by copying or moving
1064 to a new track, we want to replace the old selection with the new ones
1067 if (new_views.size() > 0) {
1068 _editor->selection->set (new_views);
1071 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1075 /* write commands for the accumulated diffs for all our modified playlists */
1076 add_stateful_diff_commands_for_playlists (modified_playlists);
1078 _editor->commit_reversible_command ();
1081 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1082 * @param region Region to remove.
1083 * @param playlist playlist To remove from.
1084 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1085 * that clear_changes () is only called once per playlist.
1088 RegionMoveDrag::remove_region_from_playlist (
1089 boost::shared_ptr<Region> region,
1090 boost::shared_ptr<Playlist> playlist,
1091 PlaylistSet& modified_playlists
1094 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1097 playlist->clear_changes ();
1100 playlist->remove_region (region);
1104 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1105 * clearing the playlist's diff history first if necessary.
1106 * @param region Region to insert.
1107 * @param dest_rtv Destination RouteTimeAxisView.
1108 * @param dest_layer Destination layer.
1109 * @param where Destination position.
1110 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1111 * that clear_changes () is only called once per playlist.
1112 * @return New RegionView, or 0 if no insert was performed.
1115 RegionMoveDrag::insert_region_into_playlist (
1116 boost::shared_ptr<Region> region,
1117 RouteTimeAxisView* dest_rtv,
1120 PlaylistSet& modified_playlists
1123 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1124 if (!dest_playlist) {
1128 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1129 _new_region_view = 0;
1130 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1132 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1133 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1135 dest_playlist->clear_changes ();
1138 dest_playlist->add_region (region, where);
1140 if (dest_rtv->view()->layer_display() == Stacked) {
1141 region->set_layer (dest_layer);
1142 region->set_pending_explicit_relayer (true);
1147 assert (_new_region_view);
1149 return _new_region_view;
1153 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1155 _new_region_view = rv;
1159 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1161 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1162 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1164 _editor->session()->add_command (c);
1173 RegionMoveDrag::aborted (bool movement_occurred)
1177 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1184 RegionMotionDrag::aborted (movement_occurred);
1189 RegionMotionDrag::aborted (bool)
1191 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1192 RegionView* rv = i->view;
1193 TimeAxisView* tv = &(rv->get_time_axis_view ());
1194 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1196 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1197 rv->get_canvas_group()->property_y() = 0;
1198 rv->get_time_axis_view().reveal_dependent_views (*rv);
1199 rv->fake_set_opaque (false);
1200 rv->move (-_total_x_delta, 0);
1201 rv->set_height (rtv->view()->child_height ());
1204 _editor->update_canvas_now ();
1207 /** @param b true to brush, otherwise false.
1208 * @param c true to make copies of the regions being moved, otherwise false.
1210 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1211 : RegionMotionDrag (e, i, p, v, b),
1214 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1217 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1218 if (rtv && rtv->is_track()) {
1219 speed = rtv->track()->speed ();
1222 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1226 RegionMoveDrag::setup_pointer_frame_offset ()
1228 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1231 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1232 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1234 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1236 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1237 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1239 _primary = v->view()->create_region_view (r, false, false);
1241 _primary->get_canvas_group()->show ();
1242 _primary->set_position (pos, 0);
1243 _views.push_back (DraggingView (_primary, this));
1245 _last_frame_position = pos;
1247 _item = _primary->get_canvas_group ();
1251 RegionInsertDrag::finished (GdkEvent *, bool)
1253 _editor->update_canvas_now ();
1255 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1257 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1258 _primary->get_canvas_group()->property_y() = 0;
1260 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1262 _editor->begin_reversible_command (Operations::insert_region);
1263 playlist->clear_changes ();
1264 playlist->add_region (_primary->region (), _last_frame_position);
1265 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1266 _editor->commit_reversible_command ();
1274 RegionInsertDrag::aborted (bool)
1281 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1282 : RegionMoveDrag (e, i, p, v, false, false)
1284 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1287 struct RegionSelectionByPosition {
1288 bool operator() (RegionView*a, RegionView* b) {
1289 return a->region()->position () < b->region()->position();
1294 RegionSpliceDrag::motion (GdkEvent* event, bool)
1296 /* Which trackview is this ? */
1298 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1299 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1301 /* The region motion is only processed if the pointer is over
1305 if (!tv || !tv->is_track()) {
1306 /* To make sure we hide the verbose canvas cursor when the mouse is
1307 not held over and audiotrack.
1309 _editor->verbose_cursor()->hide ();
1315 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1321 RegionSelection copy (_editor->selection->regions);
1323 RegionSelectionByPosition cmp;
1326 framepos_t const pf = adjusted_current_frame (event);
1328 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1330 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1336 boost::shared_ptr<Playlist> playlist;
1338 if ((playlist = atv->playlist()) == 0) {
1342 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1347 if (pf < (*i)->region()->last_frame() + 1) {
1351 if (pf > (*i)->region()->first_frame()) {
1357 playlist->shuffle ((*i)->region(), dir);
1362 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1364 RegionMoveDrag::finished (event, movement_occurred);
1368 RegionSpliceDrag::aborted (bool)
1373 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1375 _view (dynamic_cast<MidiTimeAxisView*> (v))
1377 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1383 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1387 _view->playlist()->freeze ();
1390 framepos_t const f = adjusted_current_frame (event);
1391 if (f < grab_frame()) {
1392 _region->set_position (f);
1395 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1396 so that if this region is duplicated, its duplicate starts on
1397 a snap point rather than 1 frame after a snap point. Otherwise things get
1398 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1399 place snapped notes at the start of the region.
1402 framecnt_t const len = abs (f - grab_frame () - 1);
1403 _region->set_length (len < 1 ? 1 : len);
1409 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1411 if (!movement_occurred) {
1414 _view->playlist()->thaw ();
1418 _editor->commit_reversible_command ();
1423 RegionCreateDrag::add_region ()
1425 if (_editor->session()) {
1426 const TempoMap& map (_editor->session()->tempo_map());
1427 framecnt_t pos = grab_frame();
1428 const Meter& m = map.meter_at (pos);
1429 /* not that the frame rate used here can be affected by pull up/down which
1432 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1433 _region = _view->add_region (grab_frame(), len, false);
1438 RegionCreateDrag::aborted (bool)
1441 _view->playlist()->thaw ();
1447 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1451 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1455 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1457 Gdk::Cursor* cursor;
1458 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1459 float x_fraction = cnote->mouse_x_fraction ();
1461 if (x_fraction > 0.0 && x_fraction < 0.25) {
1462 cursor = _editor->cursors()->left_side_trim;
1464 cursor = _editor->cursors()->right_side_trim;
1467 Drag::start_grab (event, cursor);
1469 region = &cnote->region_view();
1471 double const region_start = region->get_position_pixels();
1472 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1474 if (grab_x() <= middle_point) {
1475 cursor = _editor->cursors()->left_side_trim;
1478 cursor = _editor->cursors()->right_side_trim;
1482 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1484 if (event->motion.state & Keyboard::PrimaryModifier) {
1490 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1492 if (ms.size() > 1) {
1493 /* has to be relative, may make no sense otherwise */
1497 /* select this note; if it is already selected, preserve the existing selection,
1498 otherwise make this note the only one selected.
1500 region->note_selected (cnote, cnote->selected ());
1502 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1503 MidiRegionSelection::iterator next;
1506 (*r)->begin_resizing (at_front);
1512 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1514 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1515 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1516 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1521 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1523 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1524 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1525 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1530 NoteResizeDrag::aborted (bool)
1532 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1533 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1534 (*r)->abort_resizing ();
1538 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1541 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1545 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1551 RegionGainDrag::finished (GdkEvent *, bool)
1557 RegionGainDrag::aborted (bool)
1562 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1563 : RegionDrag (e, i, p, v)
1565 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1569 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1572 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1573 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1575 if (tv && tv->is_track()) {
1576 speed = tv->track()->speed();
1579 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1580 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1581 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1583 framepos_t const pf = adjusted_current_frame (event);
1585 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1586 /* Move the contents of the region around without changing the region bounds */
1587 _operation = ContentsTrim;
1588 Drag::start_grab (event, _editor->cursors()->trimmer);
1590 /* These will get overridden for a point trim.*/
1591 if (pf < (region_start + region_length/2)) {
1592 /* closer to front */
1593 _operation = StartTrim;
1594 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1597 _operation = EndTrim;
1598 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1602 switch (_operation) {
1604 show_verbose_cursor_time (region_start);
1605 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1606 i->view->trim_front_starting ();
1610 show_verbose_cursor_time (region_end);
1613 show_verbose_cursor_time (pf);
1617 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1618 i->view->region()->suspend_property_changes ();
1623 TrimDrag::motion (GdkEvent* event, bool first_move)
1625 RegionView* rv = _primary;
1628 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1629 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1630 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1632 if (tv && tv->is_track()) {
1633 speed = tv->track()->speed();
1636 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1642 switch (_operation) {
1644 trim_type = "Region start trim";
1647 trim_type = "Region end trim";
1650 trim_type = "Region content trim";
1654 _editor->begin_reversible_command (trim_type);
1656 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1657 RegionView* rv = i->view;
1658 rv->fake_set_opaque (false);
1659 rv->enable_display (false);
1660 rv->region()->playlist()->clear_owned_changes ();
1662 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1665 arv->temporarily_hide_envelope ();
1668 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1669 insert_result = _editor->motion_frozen_playlists.insert (pl);
1671 if (insert_result.second) {
1677 bool non_overlap_trim = false;
1679 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1680 non_overlap_trim = true;
1683 switch (_operation) {
1685 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1686 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1691 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1692 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1698 bool swap_direction = false;
1700 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1701 swap_direction = true;
1704 framecnt_t frame_delta = 0;
1706 bool left_direction = false;
1707 if (last_pointer_frame() > adjusted_current_frame(event)) {
1708 left_direction = true;
1711 if (left_direction) {
1712 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1714 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1718 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1724 switch (_operation) {
1726 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1729 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1732 show_verbose_cursor_time (adjusted_current_frame (event));
1739 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1741 if (movement_occurred) {
1742 motion (event, false);
1744 /* This must happen before the region's StatefulDiffCommand is created, as it may
1745 `correct' (ahem) the region's _start from being negative to being zero. It
1746 needs to be zero in the undo record.
1748 if (_operation == StartTrim) {
1749 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1750 i->view->trim_front_ending ();
1754 if (!_editor->selection->selected (_primary)) {
1755 _primary->thaw_after_trim ();
1758 set<boost::shared_ptr<Playlist> > diffed_playlists;
1760 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1761 i->view->thaw_after_trim ();
1762 i->view->enable_display (true);
1763 i->view->fake_set_opaque (true);
1765 /* Trimming one region may affect others on the playlist, so we need
1766 to get undo Commands from the whole playlist rather than just the
1767 region. Use diffed_playlists to make sure we don't diff a given
1768 playlist more than once.
1770 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1771 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1772 vector<Command*> cmds;
1774 _editor->session()->add_commands (cmds);
1775 diffed_playlists.insert (p);
1779 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1783 _editor->motion_frozen_playlists.clear ();
1784 _editor->commit_reversible_command();
1787 /* no mouse movement */
1788 _editor->point_trim (event, adjusted_current_frame (event));
1791 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1792 if (_operation == StartTrim) {
1793 i->view->trim_front_ending ();
1796 i->view->region()->resume_property_changes ();
1801 TrimDrag::aborted (bool movement_occurred)
1803 /* Our motion method is changing model state, so use the Undo system
1804 to cancel. Perhaps not ideal, as this will leave an Undo point
1805 behind which may be slightly odd from the user's point of view.
1810 if (movement_occurred) {
1814 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1815 i->view->region()->resume_property_changes ();
1820 TrimDrag::setup_pointer_frame_offset ()
1822 list<DraggingView>::iterator i = _views.begin ();
1823 while (i != _views.end() && i->view != _primary) {
1827 if (i == _views.end()) {
1831 switch (_operation) {
1833 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1836 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1843 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1847 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1848 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1853 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1855 Drag::start_grab (event, cursor);
1856 show_verbose_cursor_time (adjusted_current_frame(event));
1860 MeterMarkerDrag::setup_pointer_frame_offset ()
1862 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1866 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1870 // create a dummy marker for visual representation of moving the
1871 // section, because whether its a copy or not, we're going to
1872 // leave or lose the original marker (leave if its a copy; lose if its
1873 // not, because we'll remove it from the map).
1875 MeterSection section (_marker->meter());
1877 if (!section.movable()) {
1882 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1884 _marker = new MeterMarker (
1886 *_editor->meter_group,
1887 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1889 *new MeterSection (_marker->meter())
1892 /* use the new marker for the grab */
1893 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1896 TempoMap& map (_editor->session()->tempo_map());
1897 /* remove the section while we drag it */
1898 map.remove_meter (section);
1902 framepos_t const pf = adjusted_current_frame (event);
1903 _marker->set_position (pf);
1904 show_verbose_cursor_time (pf);
1908 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1910 if (!movement_occurred) {
1914 motion (event, false);
1916 Timecode::BBT_Time when;
1918 TempoMap& map (_editor->session()->tempo_map());
1919 map.bbt_time (last_pointer_frame(), when);
1921 if (_copy == true) {
1922 _editor->begin_reversible_command (_("copy meter mark"));
1923 XMLNode &before = map.get_state();
1924 map.add_meter (_marker->meter(), when);
1925 XMLNode &after = map.get_state();
1926 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1927 _editor->commit_reversible_command ();
1930 _editor->begin_reversible_command (_("move meter mark"));
1931 XMLNode &before = map.get_state();
1933 /* we removed it before, so add it back now */
1935 map.add_meter (_marker->meter(), when);
1936 XMLNode &after = map.get_state();
1937 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1938 _editor->commit_reversible_command ();
1941 // delete the dummy marker we used for visual representation while moving.
1942 // a new visual marker will show up automatically.
1947 MeterMarkerDrag::aborted (bool moved)
1949 _marker->set_position (_marker->meter().frame ());
1952 TempoMap& map (_editor->session()->tempo_map());
1953 /* we removed it before, so add it back now */
1954 map.add_meter (_marker->meter(), _marker->meter().frame());
1955 // delete the dummy marker we used for visual representation while moving.
1956 // a new visual marker will show up automatically.
1961 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1965 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1967 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1972 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1974 Drag::start_grab (event, cursor);
1975 show_verbose_cursor_time (adjusted_current_frame (event));
1979 TempoMarkerDrag::setup_pointer_frame_offset ()
1981 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1985 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
1989 // create a dummy marker for visual representation of moving the
1990 // section, because whether its a copy or not, we're going to
1991 // leave or lose the original marker (leave if its a copy; lose if its
1992 // not, because we'll remove it from the map).
1994 // create a dummy marker for visual representation of moving the copy.
1995 // The actual copying is not done before we reach the finish callback.
1998 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2000 TempoSection section (_marker->tempo());
2002 _marker = new TempoMarker (
2004 *_editor->tempo_group,
2005 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2007 *new TempoSection (_marker->tempo())
2010 /* use the new marker for the grab */
2011 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2014 TempoMap& map (_editor->session()->tempo_map());
2015 /* remove the section while we drag it */
2016 map.remove_tempo (section);
2020 framepos_t const pf = adjusted_current_frame (event);
2021 _marker->set_position (pf);
2022 show_verbose_cursor_time (pf);
2026 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2028 if (!movement_occurred) {
2032 motion (event, false);
2034 Timecode::BBT_Time when;
2036 TempoMap& map (_editor->session()->tempo_map());
2037 map.bbt_time (last_pointer_frame(), when);
2039 if (_copy == true) {
2040 _editor->begin_reversible_command (_("copy tempo mark"));
2041 XMLNode &before = map.get_state();
2042 map.add_tempo (_marker->tempo(), when);
2043 XMLNode &after = map.get_state();
2044 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2045 _editor->commit_reversible_command ();
2048 _editor->begin_reversible_command (_("move tempo mark"));
2049 XMLNode &before = map.get_state();
2050 /* we removed it before, so add it back now */
2051 map.add_tempo (_marker->tempo(), when);
2052 XMLNode &after = map.get_state();
2053 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2054 _editor->commit_reversible_command ();
2057 // delete the dummy marker we used for visual representation while moving.
2058 // a new visual marker will show up automatically.
2063 TempoMarkerDrag::aborted (bool moved)
2065 _marker->set_position (_marker->tempo().frame());
2067 TempoMap& map (_editor->session()->tempo_map());
2068 /* we removed it before, so add it back now */
2069 map.add_tempo (_marker->tempo(), _marker->tempo().frame());
2070 // delete the dummy marker we used for visual representation while moving.
2071 // a new visual marker will show up automatically.
2076 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2080 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2083 /** Do all the things we do when dragging the playhead to make it look as though
2084 * we have located, without actually doing the locate (because that would cause
2085 * the diskstream buffers to be refilled, which is too slow).
2088 CursorDrag::fake_locate (framepos_t t)
2090 _editor->playhead_cursor->set_position (t);
2092 Session* s = _editor->session ();
2093 if (s->timecode_transmission_suspended ()) {
2094 framepos_t const f = _editor->playhead_cursor->current_frame;
2095 s->send_mmc_locate (f);
2096 s->send_full_time_code (f);
2099 show_verbose_cursor_time (t);
2100 _editor->UpdateAllTransportClocks (t);
2104 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2106 Drag::start_grab (event, c);
2108 _grab_zoom = _editor->frames_per_unit;
2110 framepos_t where = _editor->event_frame (event, 0, 0);
2111 _editor->snap_to_with_modifier (where, event);
2113 _editor->_dragging_playhead = true;
2115 Session* s = _editor->session ();
2118 if (_was_rolling && _stop) {
2122 if (s->is_auditioning()) {
2123 s->cancel_audition ();
2126 s->request_suspend_timecode_transmission ();
2127 while (!s->timecode_transmission_suspended ()) {
2128 /* twiddle our thumbs */
2132 fake_locate (where);
2136 CursorDrag::motion (GdkEvent* event, bool)
2138 framepos_t const adjusted_frame = adjusted_current_frame (event);
2139 if (adjusted_frame != last_pointer_frame()) {
2140 fake_locate (adjusted_frame);
2142 _editor->update_canvas_now ();
2148 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2150 _editor->_dragging_playhead = false;
2152 if (!movement_occurred && _stop) {
2156 motion (event, false);
2158 Session* s = _editor->session ();
2160 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2161 _editor->_pending_locate_request = true;
2162 s->request_resume_timecode_transmission ();
2167 CursorDrag::aborted (bool)
2169 if (_editor->_dragging_playhead) {
2170 _editor->session()->request_resume_timecode_transmission ();
2171 _editor->_dragging_playhead = false;
2174 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2177 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2178 : RegionDrag (e, i, p, v)
2180 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2184 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2186 Drag::start_grab (event, cursor);
2188 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2189 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2191 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2193 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2197 FadeInDrag::setup_pointer_frame_offset ()
2199 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2200 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2201 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2205 FadeInDrag::motion (GdkEvent* event, bool)
2207 framecnt_t fade_length;
2209 framepos_t const pos = adjusted_current_frame (event);
2211 boost::shared_ptr<Region> region = _primary->region ();
2213 if (pos < (region->position() + 64)) {
2214 fade_length = 64; // this should be a minimum defined somewhere
2215 } else if (pos > region->last_frame()) {
2216 fade_length = region->length();
2218 fade_length = pos - region->position();
2221 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2223 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2229 tmp->reset_fade_in_shape_width (fade_length);
2230 tmp->show_fade_line((framecnt_t) fade_length);
2233 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2237 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2239 if (!movement_occurred) {
2243 framecnt_t fade_length;
2245 framepos_t const pos = adjusted_current_frame (event);
2247 boost::shared_ptr<Region> region = _primary->region ();
2249 if (pos < (region->position() + 64)) {
2250 fade_length = 64; // this should be a minimum defined somewhere
2251 } else if (pos > region->last_frame()) {
2252 fade_length = region->length();
2254 fade_length = pos - region->position();
2257 _editor->begin_reversible_command (_("change fade in length"));
2259 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2261 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2267 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2268 XMLNode &before = alist->get_state();
2270 tmp->audio_region()->set_fade_in_length (fade_length);
2271 tmp->audio_region()->set_fade_in_active (true);
2272 tmp->hide_fade_line();
2274 XMLNode &after = alist->get_state();
2275 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2278 _editor->commit_reversible_command ();
2282 FadeInDrag::aborted (bool)
2284 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2285 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2291 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2292 tmp->hide_fade_line();
2296 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2297 : RegionDrag (e, i, p, v)
2299 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2303 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2305 Drag::start_grab (event, cursor);
2307 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2308 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2310 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2312 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2316 FadeOutDrag::setup_pointer_frame_offset ()
2318 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2319 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2320 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2324 FadeOutDrag::motion (GdkEvent* event, bool)
2326 framecnt_t fade_length;
2328 framepos_t const pos = adjusted_current_frame (event);
2330 boost::shared_ptr<Region> region = _primary->region ();
2332 if (pos > (region->last_frame() - 64)) {
2333 fade_length = 64; // this should really be a minimum fade defined somewhere
2335 else if (pos < region->position()) {
2336 fade_length = region->length();
2339 fade_length = region->last_frame() - pos;
2342 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2344 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2350 tmp->reset_fade_out_shape_width (fade_length);
2351 tmp->show_fade_line(region->length() - fade_length);
2354 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2358 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2360 if (!movement_occurred) {
2364 framecnt_t fade_length;
2366 framepos_t const pos = adjusted_current_frame (event);
2368 boost::shared_ptr<Region> region = _primary->region ();
2370 if (pos > (region->last_frame() - 64)) {
2371 fade_length = 64; // this should really be a minimum fade defined somewhere
2373 else if (pos < region->position()) {
2374 fade_length = region->length();
2377 fade_length = region->last_frame() - pos;
2380 _editor->begin_reversible_command (_("change fade out length"));
2382 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2384 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2390 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2391 XMLNode &before = alist->get_state();
2393 tmp->audio_region()->set_fade_out_length (fade_length);
2394 tmp->audio_region()->set_fade_out_active (true);
2395 tmp->hide_fade_line();
2397 XMLNode &after = alist->get_state();
2398 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2401 _editor->commit_reversible_command ();
2405 FadeOutDrag::aborted (bool)
2407 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2408 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2414 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2415 tmp->hide_fade_line();
2419 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2422 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2424 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2427 _points.push_back (Gnome::Art::Point (0, 0));
2428 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2431 MarkerDrag::~MarkerDrag ()
2433 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2439 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2441 Drag::start_grab (event, cursor);
2445 Location *location = _editor->find_location_from_marker (_marker, is_start);
2446 _editor->_dragging_edit_point = true;
2448 update_item (location);
2450 // _drag_line->show();
2451 // _line->raise_to_top();
2454 show_verbose_cursor_time (location->start());
2456 show_verbose_cursor_time (location->end());
2459 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2462 case Selection::Toggle:
2463 _editor->selection->toggle (_marker);
2465 case Selection::Set:
2466 if (!_editor->selection->selected (_marker)) {
2467 _editor->selection->set (_marker);
2470 case Selection::Extend:
2472 Locations::LocationList ll;
2473 list<Marker*> to_add;
2475 _editor->selection->markers.range (s, e);
2476 s = min (_marker->position(), s);
2477 e = max (_marker->position(), e);
2480 if (e < max_framepos) {
2483 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2484 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2485 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2488 to_add.push_back (lm->start);
2491 to_add.push_back (lm->end);
2495 if (!to_add.empty()) {
2496 _editor->selection->add (to_add);
2500 case Selection::Add:
2501 _editor->selection->add (_marker);
2505 /* Set up copies for us to manipulate during the drag */
2507 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2508 Location* l = _editor->find_location_from_marker (*i, is_start);
2509 _copied_locations.push_back (new Location (*l));
2514 MarkerDrag::setup_pointer_frame_offset ()
2517 Location *location = _editor->find_location_from_marker (_marker, is_start);
2518 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2522 MarkerDrag::motion (GdkEvent* event, bool)
2524 framecnt_t f_delta = 0;
2526 bool move_both = false;
2528 Location *real_location;
2529 Location *copy_location = 0;
2531 framepos_t const newframe = adjusted_current_frame (event);
2533 framepos_t next = newframe;
2535 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2539 MarkerSelection::iterator i;
2540 list<Location*>::iterator x;
2542 /* find the marker we're dragging, and compute the delta */
2544 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2545 x != _copied_locations.end() && i != _editor->selection->markers.end();
2551 if (marker == _marker) {
2553 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2558 if (real_location->is_mark()) {
2559 f_delta = newframe - copy_location->start();
2563 switch (marker->type()) {
2564 case Marker::SessionStart:
2565 case Marker::RangeStart:
2566 case Marker::LoopStart:
2567 case Marker::PunchIn:
2568 f_delta = newframe - copy_location->start();
2571 case Marker::SessionEnd:
2572 case Marker::RangeEnd:
2573 case Marker::LoopEnd:
2574 case Marker::PunchOut:
2575 f_delta = newframe - copy_location->end();
2578 /* what kind of marker is this ? */
2586 if (i == _editor->selection->markers.end()) {
2587 /* hmm, impossible - we didn't find the dragged marker */
2591 /* now move them all */
2593 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2594 x != _copied_locations.end() && i != _editor->selection->markers.end();
2600 /* call this to find out if its the start or end */
2602 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2606 if (real_location->locked()) {
2610 if (copy_location->is_mark()) {
2614 copy_location->set_start (copy_location->start() + f_delta);
2618 framepos_t new_start = copy_location->start() + f_delta;
2619 framepos_t new_end = copy_location->end() + f_delta;
2621 if (is_start) { // start-of-range marker
2624 copy_location->set_start (new_start);
2625 copy_location->set_end (new_end);
2626 } else if (new_start < copy_location->end()) {
2627 copy_location->set_start (new_start);
2628 } else if (newframe > 0) {
2629 _editor->snap_to (next, 1, true);
2630 copy_location->set_end (next);
2631 copy_location->set_start (newframe);
2634 } else { // end marker
2637 copy_location->set_end (new_end);
2638 copy_location->set_start (new_start);
2639 } else if (new_end > copy_location->start()) {
2640 copy_location->set_end (new_end);
2641 } else if (newframe > 0) {
2642 _editor->snap_to (next, -1, true);
2643 copy_location->set_start (next);
2644 copy_location->set_end (newframe);
2649 update_item (copy_location);
2651 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2654 lm->set_position (copy_location->start(), copy_location->end());
2658 assert (!_copied_locations.empty());
2660 show_verbose_cursor_time (newframe);
2663 _editor->update_canvas_now ();
2668 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2670 if (!movement_occurred) {
2672 /* just a click, do nothing but finish
2673 off the selection process
2676 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2679 case Selection::Set:
2680 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2681 _editor->selection->set (_marker);
2685 case Selection::Toggle:
2686 case Selection::Extend:
2687 case Selection::Add:
2694 _editor->_dragging_edit_point = false;
2696 _editor->begin_reversible_command ( _("move marker") );
2697 XMLNode &before = _editor->session()->locations()->get_state();
2699 MarkerSelection::iterator i;
2700 list<Location*>::iterator x;
2703 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2704 x != _copied_locations.end() && i != _editor->selection->markers.end();
2707 Location * location = _editor->find_location_from_marker (*i, is_start);
2711 if (location->locked()) {
2715 if (location->is_mark()) {
2716 location->set_start ((*x)->start());
2718 location->set ((*x)->start(), (*x)->end());
2723 XMLNode &after = _editor->session()->locations()->get_state();
2724 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2725 _editor->commit_reversible_command ();
2729 MarkerDrag::aborted (bool)
2735 MarkerDrag::update_item (Location*)
2740 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2742 _cumulative_x_drag (0),
2743 _cumulative_y_drag (0)
2745 if (_zero_gain_fraction < 0.0) {
2746 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2749 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2751 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2757 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2759 Drag::start_grab (event, _editor->cursors()->fader);
2761 // start the grab at the center of the control point so
2762 // the point doesn't 'jump' to the mouse after the first drag
2763 _fixed_grab_x = _point->get_x();
2764 _fixed_grab_y = _point->get_y();
2766 float const fraction = 1 - (_point->get_y() / _point->line().height());
2768 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2770 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2771 event->button.x + 10, event->button.y + 10);
2773 _editor->verbose_cursor()->show ();
2777 ControlPointDrag::motion (GdkEvent* event, bool)
2779 double dx = _drags->current_pointer_x() - last_pointer_x();
2780 double dy = _drags->current_pointer_y() - last_pointer_y();
2782 if (event->button.state & Keyboard::SecondaryModifier) {
2787 /* coordinate in pixels relative to the start of the region (for region-based automation)
2788 or track (for track-based automation) */
2789 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2790 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2792 // calculate zero crossing point. back off by .01 to stay on the
2793 // positive side of zero
2794 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2796 // make sure we hit zero when passing through
2797 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2801 if (_x_constrained) {
2804 if (_y_constrained) {
2808 _cumulative_x_drag = cx - _fixed_grab_x;
2809 _cumulative_y_drag = cy - _fixed_grab_y;
2813 cy = min ((double) _point->line().height(), cy);
2815 framepos_t cx_frames = _editor->unit_to_frame (cx);
2817 if (!_x_constrained) {
2818 _editor->snap_to_with_modifier (cx_frames, event);
2821 cx_frames = min (cx_frames, _point->line().maximum_time());
2823 float const fraction = 1.0 - (cy / _point->line().height());
2825 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2827 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2829 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2833 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2835 if (!movement_occurred) {
2839 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2840 _editor->reset_point_selection ();
2844 motion (event, false);
2847 _point->line().end_drag ();
2848 _editor->session()->commit_reversible_command ();
2852 ControlPointDrag::aborted (bool)
2854 _point->line().reset ();
2858 ControlPointDrag::active (Editing::MouseMode m)
2860 if (m == Editing::MouseGain) {
2861 /* always active in mouse gain */
2865 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2866 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2869 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2872 _cumulative_y_drag (0)
2874 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2878 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2880 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2883 _item = &_line->grab_item ();
2885 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2886 origin, and ditto for y.
2889 double cx = event->button.x;
2890 double cy = event->button.y;
2892 _line->parent_group().w2i (cx, cy);
2894 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2899 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2900 /* no adjacent points */
2904 Drag::start_grab (event, _editor->cursors()->fader);
2906 /* store grab start in parent frame */
2911 double fraction = 1.0 - (cy / _line->height());
2913 _line->start_drag_line (before, after, fraction);
2915 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2916 event->button.x + 10, event->button.y + 10);
2918 _editor->verbose_cursor()->show ();
2922 LineDrag::motion (GdkEvent* event, bool)
2924 double dy = _drags->current_pointer_y() - last_pointer_y();
2926 if (event->button.state & Keyboard::SecondaryModifier) {
2930 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2932 _cumulative_y_drag = cy - _fixed_grab_y;
2935 cy = min ((double) _line->height(), cy);
2937 double const fraction = 1.0 - (cy / _line->height());
2941 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2947 /* we are ignoring x position for this drag, so we can just pass in anything */
2948 _line->drag_motion (0, fraction, true, push);
2950 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2954 LineDrag::finished (GdkEvent* event, bool)
2956 motion (event, false);
2958 _editor->session()->commit_reversible_command ();
2962 LineDrag::aborted (bool)
2967 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2970 _cumulative_x_drag (0)
2972 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2976 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2978 Drag::start_grab (event);
2980 _line = reinterpret_cast<Line*> (_item);
2983 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2985 double cx = event->button.x;
2986 double cy = event->button.y;
2988 _item->property_parent().get_value()->w2i(cx, cy);
2990 /* store grab start in parent frame */
2991 _region_view_grab_x = cx;
2993 _before = *(float*) _item->get_data ("position");
2995 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2997 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3001 FeatureLineDrag::motion (GdkEvent*, bool)
3003 double dx = _drags->current_pointer_x() - last_pointer_x();
3005 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3007 _cumulative_x_drag += dx;
3009 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3018 ArdourCanvas::Points points;
3020 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3022 _line->get_bounds(x1, y2, x2, y2);
3024 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3025 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3027 _line->property_points() = points;
3029 float *pos = new float;
3032 _line->set_data ("position", pos);
3038 FeatureLineDrag::finished (GdkEvent*, bool)
3040 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3041 _arv->update_transient(_before, _before);
3045 FeatureLineDrag::aborted (bool)
3050 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3053 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3057 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3059 Drag::start_grab (event);
3060 show_verbose_cursor_time (adjusted_current_frame (event));
3064 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3071 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3073 framepos_t grab = grab_frame ();
3074 if (Config->get_rubberbanding_snaps_to_grid ()) {
3075 _editor->snap_to_with_modifier (grab, event);
3078 /* base start and end on initial click position */
3088 if (_drags->current_pointer_y() < grab_y()) {
3089 y1 = _drags->current_pointer_y();
3092 y2 = _drags->current_pointer_y();
3097 if (start != end || y1 != y2) {
3099 double x1 = _editor->frame_to_pixel (start);
3100 double x2 = _editor->frame_to_pixel (end);
3102 _editor->rubberband_rect->property_x1() = x1;
3103 _editor->rubberband_rect->property_y1() = y1;
3104 _editor->rubberband_rect->property_x2() = x2;
3105 _editor->rubberband_rect->property_y2() = y2;
3107 _editor->rubberband_rect->show();
3108 _editor->rubberband_rect->raise_to_top();
3110 show_verbose_cursor_time (pf);
3112 do_select_things (event, true);
3117 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3122 if (grab_frame() < last_pointer_frame()) {
3124 x2 = last_pointer_frame ();
3127 x1 = last_pointer_frame ();
3133 if (_drags->current_pointer_y() < grab_y()) {
3134 y1 = _drags->current_pointer_y();
3137 y2 = _drags->current_pointer_y();
3141 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3145 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3147 if (movement_occurred) {
3149 motion (event, false);
3150 do_select_things (event, false);
3158 _editor->rubberband_rect->hide();
3162 RubberbandSelectDrag::aborted (bool)
3164 _editor->rubberband_rect->hide ();
3167 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3168 : RegionDrag (e, i, p, v)
3170 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3174 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3176 Drag::start_grab (event, cursor);
3178 show_verbose_cursor_time (adjusted_current_frame (event));
3182 TimeFXDrag::motion (GdkEvent* event, bool)
3184 RegionView* rv = _primary;
3186 framepos_t const pf = adjusted_current_frame (event);
3188 if (pf > rv->region()->position()) {
3189 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3192 show_verbose_cursor_time (pf);
3196 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3198 _primary->get_time_axis_view().hide_timestretch ();
3200 if (!movement_occurred) {
3204 if (last_pointer_frame() < _primary->region()->position()) {
3205 /* backwards drag of the left edge - not usable */
3209 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3211 float percentage = (double) newlen / (double) _primary->region()->length();
3213 #ifndef USE_RUBBERBAND
3214 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3215 if (_primary->region()->data_type() == DataType::AUDIO) {
3216 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3220 // XXX how do timeFX on multiple regions ?
3225 if (_editor->time_stretch (rs, percentage) == -1) {
3226 error << _("An error occurred while executing time stretch operation") << endmsg;
3231 TimeFXDrag::aborted (bool)
3233 _primary->get_time_axis_view().hide_timestretch ();
3236 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3239 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3243 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3245 Drag::start_grab (event);
3249 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3251 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3255 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3257 if (movement_occurred && _editor->session()) {
3258 /* make sure we stop */
3259 _editor->session()->request_transport_speed (0.0);
3264 ScrubDrag::aborted (bool)
3269 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3273 , _original_pointer_time_axis (-1)
3274 , _last_pointer_time_axis (-1)
3276 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3280 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3282 if (_editor->session() == 0) {
3286 Gdk::Cursor* cursor = 0;
3288 switch (_operation) {
3289 case CreateSelection:
3290 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3295 cursor = _editor->cursors()->selector;
3296 Drag::start_grab (event, cursor);
3299 case SelectionStartTrim:
3300 if (_editor->clicked_axisview) {
3301 _editor->clicked_axisview->order_selection_trims (_item, true);
3303 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3306 case SelectionEndTrim:
3307 if (_editor->clicked_axisview) {
3308 _editor->clicked_axisview->order_selection_trims (_item, false);
3310 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3314 Drag::start_grab (event, cursor);
3318 if (_operation == SelectionMove) {
3319 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3321 show_verbose_cursor_time (adjusted_current_frame (event));
3324 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3328 SelectionDrag::setup_pointer_frame_offset ()
3330 switch (_operation) {
3331 case CreateSelection:
3332 _pointer_frame_offset = 0;
3335 case SelectionStartTrim:
3337 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3340 case SelectionEndTrim:
3341 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3347 SelectionDrag::motion (GdkEvent* event, bool first_move)
3349 framepos_t start = 0;
3353 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3354 if (pending_time_axis.first == 0) {
3358 framepos_t const pending_position = adjusted_current_frame (event);
3360 /* only alter selection if things have changed */
3362 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3366 switch (_operation) {
3367 case CreateSelection:
3369 framepos_t grab = grab_frame ();
3372 _editor->snap_to (grab);
3375 if (pending_position < grab_frame()) {
3376 start = pending_position;
3379 end = pending_position;
3383 /* first drag: Either add to the selection
3384 or create a new selection
3390 /* adding to the selection */
3391 _editor->set_selected_track_as_side_effect (Selection::Add);
3392 //_editor->selection->add (_editor->clicked_axisview);
3393 _editor->clicked_selection = _editor->selection->add (start, end);
3398 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3399 //_editor->selection->set (_editor->clicked_axisview);
3400 _editor->set_selected_track_as_side_effect (Selection::Set);
3403 _editor->clicked_selection = _editor->selection->set (start, end);
3407 /* select the track that we're in */
3408 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3409 // _editor->set_selected_track_as_side_effect (Selection::Add);
3410 _editor->selection->add (pending_time_axis.first);
3411 _added_time_axes.push_back (pending_time_axis.first);
3414 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3415 tracks that we selected in the first place.
3418 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3419 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3421 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3422 while (i != _added_time_axes.end()) {
3424 list<TimeAxisView*>::iterator tmp = i;
3427 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3428 _editor->selection->remove (*i);
3429 _added_time_axes.remove (*i);
3438 case SelectionStartTrim:
3440 start = _editor->selection->time[_editor->clicked_selection].start;
3441 end = _editor->selection->time[_editor->clicked_selection].end;
3443 if (pending_position > end) {
3446 start = pending_position;
3450 case SelectionEndTrim:
3452 start = _editor->selection->time[_editor->clicked_selection].start;
3453 end = _editor->selection->time[_editor->clicked_selection].end;
3455 if (pending_position < start) {
3458 end = pending_position;
3465 start = _editor->selection->time[_editor->clicked_selection].start;
3466 end = _editor->selection->time[_editor->clicked_selection].end;
3468 length = end - start;
3470 start = pending_position;
3471 _editor->snap_to (start);
3473 end = start + length;
3478 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3479 _editor->start_canvas_autoscroll (1, 0);
3483 _editor->selection->replace (_editor->clicked_selection, start, end);
3486 if (_operation == SelectionMove) {
3487 show_verbose_cursor_time(start);
3489 show_verbose_cursor_time(pending_position);
3494 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3496 Session* s = _editor->session();
3498 if (movement_occurred) {
3499 motion (event, false);
3500 /* XXX this is not object-oriented programming at all. ick */
3501 if (_editor->selection->time.consolidate()) {
3502 _editor->selection->TimeChanged ();
3505 /* XXX what if its a music time selection? */
3506 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3507 s->request_play_range (&_editor->selection->time, true);
3512 /* just a click, no pointer movement.*/
3514 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3515 _editor->selection->clear_time();
3518 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3519 _editor->selection->set (_editor->clicked_axisview);
3522 if (s && s->get_play_range () && s->transport_rolling()) {
3523 s->request_stop (false, false);
3528 _editor->stop_canvas_autoscroll ();
3532 SelectionDrag::aborted (bool)
3537 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3542 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3544 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3545 physical_screen_height (_editor->get_window()));
3546 _drag_rect->hide ();
3548 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3549 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3553 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3555 if (_editor->session() == 0) {
3559 Gdk::Cursor* cursor = 0;
3561 if (!_editor->temp_location) {
3562 _editor->temp_location = new Location (*_editor->session());
3565 switch (_operation) {
3566 case CreateRangeMarker:
3567 case CreateTransportMarker:
3568 case CreateCDMarker:
3570 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3575 cursor = _editor->cursors()->selector;
3579 Drag::start_grab (event, cursor);
3581 show_verbose_cursor_time (adjusted_current_frame (event));
3585 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3587 framepos_t start = 0;
3589 ArdourCanvas::SimpleRect *crect;
3591 switch (_operation) {
3592 case CreateRangeMarker:
3593 crect = _editor->range_bar_drag_rect;
3595 case CreateTransportMarker:
3596 crect = _editor->transport_bar_drag_rect;
3598 case CreateCDMarker:
3599 crect = _editor->cd_marker_bar_drag_rect;
3602 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3607 framepos_t const pf = adjusted_current_frame (event);
3609 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3610 framepos_t grab = grab_frame ();
3611 _editor->snap_to (grab);
3613 if (pf < grab_frame()) {
3621 /* first drag: Either add to the selection
3622 or create a new selection.
3627 _editor->temp_location->set (start, end);
3631 update_item (_editor->temp_location);
3633 //_drag_rect->raise_to_top();
3638 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3639 _editor->start_canvas_autoscroll (1, 0);
3643 _editor->temp_location->set (start, end);
3645 double x1 = _editor->frame_to_pixel (start);
3646 double x2 = _editor->frame_to_pixel (end);
3647 crect->property_x1() = x1;
3648 crect->property_x2() = x2;
3650 update_item (_editor->temp_location);
3653 show_verbose_cursor_time (pf);
3658 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3660 Location * newloc = 0;
3664 if (movement_occurred) {
3665 motion (event, false);
3668 switch (_operation) {
3669 case CreateRangeMarker:
3670 case CreateCDMarker:
3672 _editor->begin_reversible_command (_("new range marker"));
3673 XMLNode &before = _editor->session()->locations()->get_state();
3674 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3675 if (_operation == CreateCDMarker) {
3676 flags = Location::IsRangeMarker | Location::IsCDMarker;
3677 _editor->cd_marker_bar_drag_rect->hide();
3680 flags = Location::IsRangeMarker;
3681 _editor->range_bar_drag_rect->hide();
3683 newloc = new Location (
3684 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3687 _editor->session()->locations()->add (newloc, true);
3688 XMLNode &after = _editor->session()->locations()->get_state();
3689 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3690 _editor->commit_reversible_command ();
3694 case CreateTransportMarker:
3695 // popup menu to pick loop or punch
3696 _editor->new_transport_marker_context_menu (&event->button, _item);
3700 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3702 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3707 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3709 if (end == max_framepos) {
3710 end = _editor->session()->current_end_frame ();
3713 if (start == max_framepos) {
3714 start = _editor->session()->current_start_frame ();
3717 switch (_editor->mouse_mode) {
3719 /* find the two markers on either side and then make the selection from it */
3720 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3724 /* find the two markers on either side of the click and make the range out of it */
3725 _editor->selection->set (start, end);
3734 _editor->stop_canvas_autoscroll ();
3738 RangeMarkerBarDrag::aborted (bool)
3744 RangeMarkerBarDrag::update_item (Location* location)
3746 double const x1 = _editor->frame_to_pixel (location->start());
3747 double const x2 = _editor->frame_to_pixel (location->end());
3749 _drag_rect->property_x1() = x1;
3750 _drag_rect->property_x2() = x2;
3753 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3757 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3761 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3763 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3764 Drag::start_grab (event, _editor->cursors()->zoom_out);
3767 Drag::start_grab (event, _editor->cursors()->zoom_in);
3771 show_verbose_cursor_time (adjusted_current_frame (event));
3775 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3780 framepos_t const pf = adjusted_current_frame (event);
3782 framepos_t grab = grab_frame ();
3783 _editor->snap_to_with_modifier (grab, event);
3785 /* base start and end on initial click position */
3797 _editor->zoom_rect->show();
3798 _editor->zoom_rect->raise_to_top();
3801 _editor->reposition_zoom_rect(start, end);
3803 show_verbose_cursor_time (pf);
3808 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3810 if (movement_occurred) {
3811 motion (event, false);
3813 if (grab_frame() < last_pointer_frame()) {
3814 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3816 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3819 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3820 _editor->tav_zoom_step (_zoom_out);
3822 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3826 _editor->zoom_rect->hide();
3830 MouseZoomDrag::aborted (bool)
3832 _editor->zoom_rect->hide ();
3835 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3837 , _cumulative_dx (0)
3838 , _cumulative_dy (0)
3840 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3842 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3843 _region = &_primary->region_view ();
3844 _note_height = _region->midi_stream_view()->note_height ();
3848 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3850 Drag::start_grab (event);
3852 if (!(_was_selected = _primary->selected())) {
3854 /* tertiary-click means extend selection - we'll do that on button release,
3855 so don't add it here, because otherwise we make it hard to figure
3856 out the "extend-to" range.
3859 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3862 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3865 _region->note_selected (_primary, true);
3867 _region->unique_select (_primary);
3873 /** @return Current total drag x change in frames */
3875 NoteDrag::total_dx () const
3878 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3880 /* primary note time */
3881 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3883 /* new time of the primary note in session frames */
3884 frameoffset_t st = n + dx;
3886 framepos_t const rp = _region->region()->position ();
3888 /* prevent the note being dragged earlier than the region's position */
3891 /* snap and return corresponding delta */
3892 return _region->snap_frame_to_frame (st - rp) + rp - n;
3895 /** @return Current total drag y change in note number */
3897 NoteDrag::total_dy () const
3899 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3903 NoteDrag::motion (GdkEvent *, bool)
3905 /* Total change in x and y since the start of the drag */
3906 frameoffset_t const dx = total_dx ();
3907 int8_t const dy = total_dy ();
3909 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3910 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3911 double const tdy = -dy * _note_height - _cumulative_dy;
3914 _cumulative_dx += tdx;
3915 _cumulative_dy += tdy;
3917 int8_t note_delta = total_dy();
3919 _region->move_selection (tdx, tdy, note_delta);
3921 /* the new note value may be the same as the old one, but we
3922 * don't know what that means because the selection may have
3923 * involved more than one note and we might be doing something
3924 * odd with them. so show the note value anyway, always.
3928 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3930 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3931 (int) floor (new_note));
3933 show_verbose_cursor_text (buf);
3938 NoteDrag::finished (GdkEvent* ev, bool moved)
3941 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3943 if (_was_selected) {
3944 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3946 _region->note_deselected (_primary);
3949 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3950 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3952 if (!extend && !add && _region->selection_size() > 1) {
3953 _region->unique_select (_primary);
3954 } else if (extend) {
3955 _region->note_selected (_primary, true, true);
3957 /* it was added during button press */
3962 _region->note_dropped (_primary, total_dx(), total_dy());
3967 NoteDrag::aborted (bool)
3972 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3973 : Drag (editor, item)
3975 , _nothing_to_drag (false)
3977 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3979 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3982 /* get all lines in the automation view */
3983 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3985 /* find those that overlap the ranges being dragged */
3986 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3987 while (i != lines.end ()) {
3988 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3991 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3993 /* check this range against all the AudioRanges that we are using */
3994 list<AudioRange>::const_iterator k = _ranges.begin ();
3995 while (k != _ranges.end()) {
3996 if (k->coverage (r.first, r.second) != OverlapNone) {
4002 /* add it to our list if it overlaps at all */
4003 if (k != _ranges.end()) {
4008 _lines.push_back (n);
4014 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4018 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4020 Drag::start_grab (event, cursor);
4022 /* Get line states before we start changing things */
4023 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4024 i->state = &i->line->get_state ();
4027 if (_ranges.empty()) {
4029 /* No selected time ranges: drag all points */
4030 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4031 uint32_t const N = i->line->npoints ();
4032 for (uint32_t j = 0; j < N; ++j) {
4033 i->points.push_back (i->line->nth (j));
4039 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4041 framecnt_t const half = (i->start + i->end) / 2;
4043 /* find the line that this audio range starts in */
4044 list<Line>::iterator j = _lines.begin();
4045 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4049 if (j != _lines.end()) {
4050 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4052 /* j is the line that this audio range starts in; fade into it;
4053 64 samples length plucked out of thin air.
4056 framepos_t a = i->start + 64;
4061 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4062 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4064 the_list->add (p, the_list->eval (p));
4065 j->line->add_always_in_view (p);
4066 the_list->add (q, the_list->eval (q));
4067 j->line->add_always_in_view (q);
4070 /* same thing for the end */
4073 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4077 if (j != _lines.end()) {
4078 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4080 /* j is the line that this audio range starts in; fade out of it;
4081 64 samples length plucked out of thin air.
4084 framepos_t b = i->end - 64;
4089 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4090 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4092 the_list->add (p, the_list->eval (p));
4093 j->line->add_always_in_view (p);
4094 the_list->add (q, the_list->eval (q));
4095 j->line->add_always_in_view (q);
4099 _nothing_to_drag = true;
4101 /* Find all the points that should be dragged and put them in the relevant
4102 points lists in the Line structs.
4105 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4107 uint32_t const N = i->line->npoints ();
4108 for (uint32_t j = 0; j < N; ++j) {
4110 /* here's a control point on this line */
4111 ControlPoint* p = i->line->nth (j);
4112 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4114 /* see if it's inside a range */
4115 list<AudioRange>::const_iterator k = _ranges.begin ();
4116 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4120 if (k != _ranges.end()) {
4121 /* dragging this point */
4122 _nothing_to_drag = false;
4123 i->points.push_back (p);
4129 if (_nothing_to_drag) {
4133 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4134 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4139 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4141 if (_nothing_to_drag) {
4145 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4146 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4148 /* we are ignoring x position for this drag, so we can just pass in anything */
4149 i->line->drag_motion (0, f, true, false);
4154 AutomationRangeDrag::finished (GdkEvent* event, bool)
4156 if (_nothing_to_drag) {
4160 motion (event, false);
4161 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4162 i->line->end_drag ();
4163 i->line->clear_always_in_view ();
4166 _editor->session()->commit_reversible_command ();
4170 AutomationRangeDrag::aborted (bool)
4172 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4173 i->line->clear_always_in_view ();
4178 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4181 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4182 layer = v->region()->layer ();
4183 initial_y = v->get_canvas_group()->property_y ();
4184 initial_playlist = v->region()->playlist ();
4185 initial_position = v->region()->position ();
4186 initial_end = v->region()->position () + v->region()->length ();
4189 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4193 , _cumulative_dx (0)
4195 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4199 PatchChangeDrag::motion (GdkEvent* ev, bool)
4201 framepos_t f = adjusted_current_frame (ev);
4202 boost::shared_ptr<Region> r = _region_view->region ();
4203 f = max (f, r->position ());
4204 f = min (f, r->last_frame ());
4206 framecnt_t const dxf = f - grab_frame();
4207 double const dxu = _editor->frame_to_unit (dxf);
4208 _patch_change->move (dxu - _cumulative_dx, 0);
4209 _cumulative_dx = dxu;
4213 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4215 if (!movement_occurred) {
4219 boost::shared_ptr<Region> r (_region_view->region ());
4221 framepos_t f = adjusted_current_frame (ev);
4222 f = max (f, r->position ());
4223 f = min (f, r->last_frame ());
4225 _region_view->move_patch_change (
4227 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4232 PatchChangeDrag::aborted (bool)
4234 _patch_change->move (-_cumulative_dx, 0);
4238 PatchChangeDrag::setup_pointer_frame_offset ()
4240 boost::shared_ptr<Region> region = _region_view->region ();
4241 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4244 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4245 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4252 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4254 framepos_t const p = _region_view->region()->position ();
4255 double const y = _region_view->midi_view()->y_position ();
4257 x1 = max ((framepos_t) 0, x1 - p);
4258 x2 = max ((framepos_t) 0, x2 - p);
4259 y1 = max (0.0, y1 - y);
4260 y2 = max (0.0, y2 - y);
4262 _region_view->update_drag_selection (
4263 _editor->frame_to_pixel (x1),
4264 _editor->frame_to_pixel (x2),
4267 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4272 MidiRubberbandSelectDrag::deselect_things ()
4277 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4278 : RubberbandSelectDrag (e, i)
4284 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4286 if (drag_in_progress) {
4287 /* We just want to select things at the end of the drag, not during it */
4291 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4293 _editor->begin_reversible_command (_("rubberband selection"));
4294 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4295 _editor->commit_reversible_command ();
4299 EditorRubberbandSelectDrag::deselect_things ()
4301 if (!getenv("ARDOUR_SAE")) {
4302 _editor->selection->clear_tracks();
4304 _editor->selection->clear_regions();
4305 _editor->selection->clear_points ();
4306 _editor->selection->clear_lines ();
4309 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4317 NoteCreateDrag::~NoteCreateDrag ()
4323 NoteCreateDrag::grid_frames (framepos_t t) const
4326 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4331 return _region_view->region_beats_to_region_frames (grid_beats);
4335 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4337 Drag::start_grab (event, cursor);
4339 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4341 framepos_t pf = _drags->current_pointer_frame ();
4342 framecnt_t const g = grid_frames (pf);
4344 /* Hack so that we always snap to the note that we are over, instead of snapping
4345 to the next one if we're more than halfway through the one we're over.
4347 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4351 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4353 MidiStreamView* sv = _region_view->midi_stream_view ();
4354 double const x = _editor->frame_to_pixel (_note[0]);
4355 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4357 _drag_rect->property_x1() = x;
4358 _drag_rect->property_y1() = y;
4359 _drag_rect->property_x2() = x;
4360 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4362 _drag_rect->property_outline_what() = 0xff;
4363 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4364 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4368 NoteCreateDrag::motion (GdkEvent* event, bool)
4370 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4371 double const x = _editor->frame_to_pixel (_note[1]);
4372 if (_note[1] > _note[0]) {
4373 _drag_rect->property_x2() = x;
4375 _drag_rect->property_x1() = x;
4380 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4382 if (!had_movement) {
4386 framepos_t const start = min (_note[0], _note[1]);
4387 framecnt_t length = abs (_note[0] - _note[1]);
4389 framecnt_t const g = grid_frames (start);
4390 if (_editor->snap_mode() == SnapNormal && length < g) {
4394 _region_view->create_note_at (start, _drag_rect->property_y1(), _region_view->region_frames_to_region_beats (length), true, false);
4398 NoteCreateDrag::y_to_region (double y) const
4401 _region_view->get_canvas_group()->w2i (x, y);
4406 NoteCreateDrag::aborted (bool)