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 bool const changed_position = (_last_frame_position != _primary->region()->position());
810 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
811 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
813 _editor->update_canvas_now ();
835 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
837 RegionSelection new_views;
838 PlaylistSet modified_playlists;
839 list<RegionView*> views_to_delete;
842 /* all changes were made during motion event handlers */
844 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
848 _editor->commit_reversible_command ();
852 if (_x_constrained) {
853 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
855 _editor->begin_reversible_command (Operations::region_copy);
858 /* insert the regions into their new playlists */
859 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
861 if (i->view->region()->locked()) {
867 if (changed_position && !_x_constrained) {
868 where = i->view->region()->position() - drag_delta;
870 where = i->view->region()->position();
873 RegionView* new_view = insert_region_into_playlist (
874 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
881 new_views.push_back (new_view);
883 /* we don't need the copied RegionView any more */
884 views_to_delete.push_back (i->view);
887 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
888 because when views are deleted they are automagically removed from _views, which messes
891 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
895 /* If we've created new regions either by copying or moving
896 to a new track, we want to replace the old selection with the new ones
899 if (new_views.size() > 0) {
900 _editor->selection->set (new_views);
903 /* write commands for the accumulated diffs for all our modified playlists */
904 add_stateful_diff_commands_for_playlists (modified_playlists);
906 _editor->commit_reversible_command ();
910 RegionMoveDrag::finished_no_copy (
911 bool const changed_position,
912 bool const changed_tracks,
913 framecnt_t const drag_delta
916 RegionSelection new_views;
917 PlaylistSet modified_playlists;
918 PlaylistSet frozen_playlists;
921 /* all changes were made during motion event handlers */
922 _editor->commit_reversible_command ();
926 if (_x_constrained) {
927 _editor->begin_reversible_command (_("fixed time region drag"));
929 _editor->begin_reversible_command (Operations::region_drag);
932 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
934 RegionView* rv = i->view;
936 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
937 layer_t const dest_layer = i->layer;
939 if (rv->region()->locked()) {
946 if (changed_position && !_x_constrained) {
947 where = rv->region()->position() - drag_delta;
949 where = rv->region()->position();
952 if (changed_tracks) {
954 /* insert into new playlist */
956 RegionView* new_view = insert_region_into_playlist (
957 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
965 new_views.push_back (new_view);
967 /* remove from old playlist */
969 /* the region that used to be in the old playlist is not
970 moved to the new one - we use a copy of it. as a result,
971 any existing editor for the region should no longer be
974 rv->hide_region_editor();
975 rv->fake_set_opaque (false);
977 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
981 rv->region()->clear_changes ();
984 motion on the same track. plonk the previously reparented region
985 back to its original canvas group (its streamview).
986 No need to do anything for copies as they are fake regions which will be deleted.
989 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
990 rv->get_canvas_group()->property_y() = i->initial_y;
991 rv->get_time_axis_view().reveal_dependent_views (*rv);
993 /* just change the model */
995 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
997 if (dest_rtv->view()->layer_display() == Stacked) {
998 rv->region()->set_layer (dest_layer);
999 rv->region()->set_pending_explicit_relayer (true);
1002 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1004 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1007 playlist->freeze ();
1010 /* this movement may result in a crossfade being modified, so we need to get undo
1011 data from the playlist as well as the region.
1014 r = modified_playlists.insert (playlist);
1016 playlist->clear_changes ();
1019 rv->region()->set_position (where);
1021 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1024 if (changed_tracks) {
1026 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1027 was selected in all of them, then removing it from a playlist will have removed all
1028 trace of it from _views (i.e. there were N regions selected, we removed 1,
1029 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1030 corresponding regionview, and _views is now empty).
1032 This could have invalidated any and all iterators into _views.
1034 The heuristic we use here is: if the region selection is empty, break out of the loop
1035 here. if the region selection is not empty, then restart the loop because we know that
1036 we must have removed at least the region(view) we've just been working on as well as any
1037 that we processed on previous iterations.
1039 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1040 we can just iterate.
1044 if (_views.empty()) {
1055 /* If we've created new regions either by copying or moving
1056 to a new track, we want to replace the old selection with the new ones
1059 if (new_views.size() > 0) {
1060 _editor->selection->set (new_views);
1063 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1067 /* write commands for the accumulated diffs for all our modified playlists */
1068 add_stateful_diff_commands_for_playlists (modified_playlists);
1070 _editor->commit_reversible_command ();
1073 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1074 * @param region Region to remove.
1075 * @param playlist playlist To remove from.
1076 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1077 * that clear_changes () is only called once per playlist.
1080 RegionMoveDrag::remove_region_from_playlist (
1081 boost::shared_ptr<Region> region,
1082 boost::shared_ptr<Playlist> playlist,
1083 PlaylistSet& modified_playlists
1086 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1089 playlist->clear_changes ();
1092 playlist->remove_region (region);
1096 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1097 * clearing the playlist's diff history first if necessary.
1098 * @param region Region to insert.
1099 * @param dest_rtv Destination RouteTimeAxisView.
1100 * @param dest_layer Destination layer.
1101 * @param where Destination position.
1102 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1103 * that clear_changes () is only called once per playlist.
1104 * @return New RegionView, or 0 if no insert was performed.
1107 RegionMoveDrag::insert_region_into_playlist (
1108 boost::shared_ptr<Region> region,
1109 RouteTimeAxisView* dest_rtv,
1112 PlaylistSet& modified_playlists
1115 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1116 if (!dest_playlist) {
1120 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1121 _new_region_view = 0;
1122 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1124 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1125 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1127 dest_playlist->clear_changes ();
1130 dest_playlist->add_region (region, where);
1132 if (dest_rtv->view()->layer_display() == Stacked) {
1133 region->set_layer (dest_layer);
1134 region->set_pending_explicit_relayer (true);
1139 assert (_new_region_view);
1141 return _new_region_view;
1145 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1147 _new_region_view = rv;
1151 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1153 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1154 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1156 _editor->session()->add_command (c);
1165 RegionMoveDrag::aborted (bool movement_occurred)
1169 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1176 RegionMotionDrag::aborted (movement_occurred);
1181 RegionMotionDrag::aborted (bool)
1183 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1184 RegionView* rv = i->view;
1185 TimeAxisView* tv = &(rv->get_time_axis_view ());
1186 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1188 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1189 rv->get_canvas_group()->property_y() = 0;
1190 rv->get_time_axis_view().reveal_dependent_views (*rv);
1191 rv->fake_set_opaque (false);
1192 rv->move (-_total_x_delta, 0);
1193 rv->set_height (rtv->view()->child_height ());
1196 _editor->update_canvas_now ();
1199 /** @param b true to brush, otherwise false.
1200 * @param c true to make copies of the regions being moved, otherwise false.
1202 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1203 : RegionMotionDrag (e, i, p, v, b),
1206 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1209 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1210 if (rtv && rtv->is_track()) {
1211 speed = rtv->track()->speed ();
1214 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1218 RegionMoveDrag::setup_pointer_frame_offset ()
1220 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1223 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1224 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1226 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1228 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1229 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1231 _primary = v->view()->create_region_view (r, false, false);
1233 _primary->get_canvas_group()->show ();
1234 _primary->set_position (pos, 0);
1235 _views.push_back (DraggingView (_primary, this));
1237 _last_frame_position = pos;
1239 _item = _primary->get_canvas_group ();
1243 RegionInsertDrag::finished (GdkEvent *, bool)
1245 _editor->update_canvas_now ();
1247 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1249 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1250 _primary->get_canvas_group()->property_y() = 0;
1252 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1254 _editor->begin_reversible_command (Operations::insert_region);
1255 playlist->clear_changes ();
1256 playlist->add_region (_primary->region (), _last_frame_position);
1257 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1258 _editor->commit_reversible_command ();
1266 RegionInsertDrag::aborted (bool)
1273 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1274 : RegionMoveDrag (e, i, p, v, false, false)
1276 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1279 struct RegionSelectionByPosition {
1280 bool operator() (RegionView*a, RegionView* b) {
1281 return a->region()->position () < b->region()->position();
1286 RegionSpliceDrag::motion (GdkEvent* event, bool)
1288 /* Which trackview is this ? */
1290 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1291 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1293 /* The region motion is only processed if the pointer is over
1297 if (!tv || !tv->is_track()) {
1298 /* To make sure we hide the verbose canvas cursor when the mouse is
1299 not held over and audiotrack.
1301 _editor->verbose_cursor()->hide ();
1307 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1313 RegionSelection copy (_editor->selection->regions);
1315 RegionSelectionByPosition cmp;
1318 framepos_t const pf = adjusted_current_frame (event);
1320 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1322 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1328 boost::shared_ptr<Playlist> playlist;
1330 if ((playlist = atv->playlist()) == 0) {
1334 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1339 if (pf < (*i)->region()->last_frame() + 1) {
1343 if (pf > (*i)->region()->first_frame()) {
1349 playlist->shuffle ((*i)->region(), dir);
1354 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1356 RegionMoveDrag::finished (event, movement_occurred);
1360 RegionSpliceDrag::aborted (bool)
1365 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1367 _view (dynamic_cast<MidiTimeAxisView*> (v))
1369 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1375 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1379 _view->playlist()->freeze ();
1382 framepos_t const f = adjusted_current_frame (event);
1383 if (f < grab_frame()) {
1384 _region->set_position (f);
1387 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1388 so that if this region is duplicated, its duplicate starts on
1389 a snap point rather than 1 frame after a snap point. Otherwise things get
1390 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1391 place snapped notes at the start of the region.
1394 framecnt_t const len = abs (f - grab_frame () - 1);
1395 _region->set_length (len < 1 ? 1 : len);
1401 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1403 if (!movement_occurred) {
1406 _view->playlist()->thaw ();
1410 _editor->commit_reversible_command ();
1415 RegionCreateDrag::add_region ()
1417 if (_editor->session()) {
1418 const TempoMap& map (_editor->session()->tempo_map());
1419 framecnt_t pos = grab_frame();
1420 const Meter& m = map.meter_at (pos);
1421 /* not that the frame rate used here can be affected by pull up/down which
1424 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1425 _region = _view->add_region (grab_frame(), len, false);
1430 RegionCreateDrag::aborted (bool)
1433 _view->playlist()->thaw ();
1439 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1443 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1447 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1449 Gdk::Cursor* cursor;
1450 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1451 float x_fraction = cnote->mouse_x_fraction ();
1453 if (x_fraction > 0.0 && x_fraction < 0.25) {
1454 cursor = _editor->cursors()->left_side_trim;
1456 cursor = _editor->cursors()->right_side_trim;
1459 Drag::start_grab (event, cursor);
1461 region = &cnote->region_view();
1463 double const region_start = region->get_position_pixels();
1464 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1466 if (grab_x() <= middle_point) {
1467 cursor = _editor->cursors()->left_side_trim;
1470 cursor = _editor->cursors()->right_side_trim;
1474 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1476 if (event->motion.state & Keyboard::PrimaryModifier) {
1482 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1484 if (ms.size() > 1) {
1485 /* has to be relative, may make no sense otherwise */
1489 /* select this note; if it is already selected, preserve the existing selection,
1490 otherwise make this note the only one selected.
1492 region->note_selected (cnote, cnote->selected ());
1494 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1495 MidiRegionSelection::iterator next;
1498 (*r)->begin_resizing (at_front);
1504 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1506 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1507 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1508 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1513 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1515 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1516 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1517 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1522 NoteResizeDrag::aborted (bool)
1527 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1530 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1534 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1540 RegionGainDrag::finished (GdkEvent *, bool)
1546 RegionGainDrag::aborted (bool)
1551 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1552 : RegionDrag (e, i, p, v)
1554 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1558 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1561 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1562 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1564 if (tv && tv->is_track()) {
1565 speed = tv->track()->speed();
1568 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1569 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1570 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1572 framepos_t const pf = adjusted_current_frame (event);
1574 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1575 /* Move the contents of the region around without changing the region bounds */
1576 _operation = ContentsTrim;
1577 Drag::start_grab (event, _editor->cursors()->trimmer);
1579 /* These will get overridden for a point trim.*/
1580 if (pf < (region_start + region_length/2)) {
1581 /* closer to front */
1582 _operation = StartTrim;
1583 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1586 _operation = EndTrim;
1587 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1591 switch (_operation) {
1593 show_verbose_cursor_time (region_start);
1594 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1595 i->view->trim_front_starting ();
1599 show_verbose_cursor_time (region_end);
1602 show_verbose_cursor_time (pf);
1606 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1607 i->view->region()->suspend_property_changes ();
1612 TrimDrag::motion (GdkEvent* event, bool first_move)
1614 RegionView* rv = _primary;
1617 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1618 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1619 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1621 if (tv && tv->is_track()) {
1622 speed = tv->track()->speed();
1625 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1631 switch (_operation) {
1633 trim_type = "Region start trim";
1636 trim_type = "Region end trim";
1639 trim_type = "Region content trim";
1643 _editor->begin_reversible_command (trim_type);
1645 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1646 RegionView* rv = i->view;
1647 rv->fake_set_opaque (false);
1648 rv->enable_display (false);
1649 rv->region()->playlist()->clear_owned_changes ();
1651 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1654 arv->temporarily_hide_envelope ();
1657 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1658 insert_result = _editor->motion_frozen_playlists.insert (pl);
1660 if (insert_result.second) {
1666 bool non_overlap_trim = false;
1668 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1669 non_overlap_trim = true;
1672 switch (_operation) {
1674 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1675 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1680 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1681 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1687 bool swap_direction = false;
1689 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1690 swap_direction = true;
1693 framecnt_t frame_delta = 0;
1695 bool left_direction = false;
1696 if (last_pointer_frame() > adjusted_current_frame(event)) {
1697 left_direction = true;
1700 if (left_direction) {
1701 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1703 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1706 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1707 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1713 switch (_operation) {
1715 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1718 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1721 show_verbose_cursor_time (adjusted_current_frame (event));
1728 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1730 if (movement_occurred) {
1731 motion (event, false);
1733 /* This must happen before the region's StatefulDiffCommand is created, as it may
1734 `correct' (ahem) the region's _start from being negative to being zero. It
1735 needs to be zero in the undo record.
1737 if (_operation == StartTrim) {
1738 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1739 i->view->trim_front_ending ();
1743 if (!_editor->selection->selected (_primary)) {
1744 _primary->thaw_after_trim ();
1747 set<boost::shared_ptr<Playlist> > diffed_playlists;
1749 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1750 i->view->thaw_after_trim ();
1751 i->view->enable_display (true);
1752 i->view->fake_set_opaque (true);
1754 /* Trimming one region may affect others on the playlist, so we need
1755 to get undo Commands from the whole playlist rather than just the
1756 region. Use diffed_playlists to make sure we don't diff a given
1757 playlist more than once.
1759 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1760 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1761 vector<Command*> cmds;
1763 _editor->session()->add_commands (cmds);
1764 diffed_playlists.insert (p);
1768 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1772 _editor->motion_frozen_playlists.clear ();
1773 _editor->commit_reversible_command();
1776 /* no mouse movement */
1777 _editor->point_trim (event, adjusted_current_frame (event));
1780 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1781 if (_operation == StartTrim) {
1782 i->view->trim_front_ending ();
1785 i->view->region()->resume_property_changes ();
1790 TrimDrag::aborted (bool movement_occurred)
1792 /* Our motion method is changing model state, so use the Undo system
1793 to cancel. Perhaps not ideal, as this will leave an Undo point
1794 behind which may be slightly odd from the user's point of view.
1799 if (movement_occurred) {
1803 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1804 i->view->region()->resume_property_changes ();
1809 TrimDrag::setup_pointer_frame_offset ()
1811 list<DraggingView>::iterator i = _views.begin ();
1812 while (i != _views.end() && i->view != _primary) {
1816 if (i == _views.end()) {
1820 switch (_operation) {
1822 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1825 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1832 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1836 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1838 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1843 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1846 // create a dummy marker for visual representation of moving the copy.
1847 // The actual copying is not done before we reach the finish callback.
1849 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1851 MeterMarker* new_marker = new MeterMarker (
1853 *_editor->meter_group,
1854 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1856 *new MeterSection (_marker->meter())
1859 _item = &new_marker->the_item ();
1860 _marker = new_marker;
1864 MetricSection& section (_marker->meter());
1866 if (!section.movable()) {
1872 Drag::start_grab (event, cursor);
1874 show_verbose_cursor_time (adjusted_current_frame(event));
1878 MeterMarkerDrag::setup_pointer_frame_offset ()
1880 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1884 MeterMarkerDrag::motion (GdkEvent* event, bool)
1886 framepos_t const pf = adjusted_current_frame (event);
1888 _marker->set_position (pf);
1890 show_verbose_cursor_time (pf);
1894 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1896 if (!movement_occurred) {
1900 motion (event, false);
1902 Timecode::BBT_Time when;
1904 TempoMap& map (_editor->session()->tempo_map());
1905 map.bbt_time (last_pointer_frame(), when);
1907 if (_copy == true) {
1908 _editor->begin_reversible_command (_("copy meter mark"));
1909 XMLNode &before = map.get_state();
1910 map.add_meter (_marker->meter(), when);
1911 XMLNode &after = map.get_state();
1912 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1913 _editor->commit_reversible_command ();
1915 // delete the dummy marker we used for visual representation of copying.
1916 // a new visual marker will show up automatically.
1919 _editor->begin_reversible_command (_("move meter mark"));
1920 XMLNode &before = map.get_state();
1921 map.move_meter (_marker->meter(), when);
1922 XMLNode &after = map.get_state();
1923 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1924 _editor->commit_reversible_command ();
1929 MeterMarkerDrag::aborted (bool)
1931 _marker->set_position (_marker->meter().frame ());
1934 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1938 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1940 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1945 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1949 // create a dummy marker for visual representation of moving the copy.
1950 // The actual copying is not done before we reach the finish callback.
1952 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1954 TempoMarker* new_marker = new TempoMarker (
1956 *_editor->tempo_group,
1957 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1959 *new TempoSection (_marker->tempo())
1962 _item = &new_marker->the_item ();
1963 _marker = new_marker;
1967 Drag::start_grab (event, cursor);
1969 show_verbose_cursor_time (adjusted_current_frame (event));
1973 TempoMarkerDrag::setup_pointer_frame_offset ()
1975 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1979 TempoMarkerDrag::motion (GdkEvent* event, bool)
1981 framepos_t const pf = adjusted_current_frame (event);
1982 _marker->set_position (pf);
1983 show_verbose_cursor_time (pf);
1987 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1989 if (!movement_occurred) {
1993 motion (event, false);
1995 Timecode::BBT_Time when;
1997 TempoMap& map (_editor->session()->tempo_map());
1998 map.bbt_time (last_pointer_frame(), when);
2000 if (_copy == true) {
2001 _editor->begin_reversible_command (_("copy tempo mark"));
2002 XMLNode &before = map.get_state();
2003 map.add_tempo (_marker->tempo(), when);
2004 XMLNode &after = map.get_state();
2005 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2006 _editor->commit_reversible_command ();
2008 // delete the dummy marker we used for visual representation of copying.
2009 // a new visual marker will show up automatically.
2012 _editor->begin_reversible_command (_("move tempo mark"));
2013 XMLNode &before = map.get_state();
2014 map.move_tempo (_marker->tempo(), when);
2015 XMLNode &after = map.get_state();
2016 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2017 _editor->commit_reversible_command ();
2022 TempoMarkerDrag::aborted (bool)
2024 _marker->set_position (_marker->tempo().frame());
2027 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2031 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2034 /** Do all the things we do when dragging the playhead to make it look as though
2035 * we have located, without actually doing the locate (because that would cause
2036 * the diskstream buffers to be refilled, which is too slow).
2039 CursorDrag::fake_locate (framepos_t t)
2041 _editor->playhead_cursor->set_position (t);
2043 Session* s = _editor->session ();
2044 if (s->timecode_transmission_suspended ()) {
2045 framepos_t const f = _editor->playhead_cursor->current_frame;
2046 s->send_mmc_locate (f);
2047 s->send_full_time_code (f);
2050 show_verbose_cursor_time (t);
2051 _editor->UpdateAllTransportClocks (t);
2055 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2057 Drag::start_grab (event, c);
2059 _grab_zoom = _editor->frames_per_unit;
2061 framepos_t where = _editor->event_frame (event, 0, 0);
2062 _editor->snap_to_with_modifier (where, event);
2064 _editor->_dragging_playhead = true;
2066 Session* s = _editor->session ();
2069 if (_was_rolling && _stop) {
2073 if (s->is_auditioning()) {
2074 s->cancel_audition ();
2077 s->request_suspend_timecode_transmission ();
2078 while (!s->timecode_transmission_suspended ()) {
2079 /* twiddle our thumbs */
2083 fake_locate (where);
2087 CursorDrag::motion (GdkEvent* event, bool)
2089 if (_drags->current_pointer_y() != last_pointer_y()) {
2091 /* zoom when we move the pointer up and down */
2093 /* y range to operate over (pixels) */
2094 double const y_range = 512;
2095 /* we will multiply the grab zoom by a factor between scale_range and scale_range^-1 */
2096 double const scale_range = 4;
2097 /* dead zone around the grab point in which to do no zooming (pixels) */
2098 double const dead_zone = 100;
2101 double dy = _drags->current_pointer_y() - grab_y();
2103 if (dy < -dead_zone || dy > dead_zone) {
2104 /* we are outside the dead zone; remove it from our calculation */
2111 /* get a number from -1 to 1 as dy ranges from -y_range to y_range */
2112 double udy = max (min (dy / y_range, 1.0), -1.0);
2114 /* and zoom, using playhead focus temporarily */
2115 Editing::ZoomFocus const zf = _editor->get_zoom_focus ();
2116 _editor->set_zoom_focus (Editing::ZoomFocusPlayhead);
2117 _editor->temporal_zoom (_grab_zoom * pow (scale_range, -udy));
2118 _editor->set_zoom_focus (zf);
2122 framepos_t const adjusted_frame = adjusted_current_frame (event);
2123 if (adjusted_frame != last_pointer_frame()) {
2124 fake_locate (adjusted_frame);
2126 _editor->update_canvas_now ();
2132 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2134 _editor->_dragging_playhead = false;
2136 if (!movement_occurred && _stop) {
2140 motion (event, false);
2142 Session* s = _editor->session ();
2144 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2145 _editor->_pending_locate_request = true;
2146 s->request_resume_timecode_transmission ();
2151 CursorDrag::aborted (bool)
2153 if (_editor->_dragging_playhead) {
2154 _editor->session()->request_resume_timecode_transmission ();
2155 _editor->_dragging_playhead = false;
2158 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2161 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2162 : RegionDrag (e, i, p, v)
2164 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2168 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2170 Drag::start_grab (event, cursor);
2172 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2173 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2175 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2177 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2181 FadeInDrag::setup_pointer_frame_offset ()
2183 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2184 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2185 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2189 FadeInDrag::motion (GdkEvent* event, bool)
2191 framecnt_t fade_length;
2193 framepos_t const pos = adjusted_current_frame (event);
2195 boost::shared_ptr<Region> region = _primary->region ();
2197 if (pos < (region->position() + 64)) {
2198 fade_length = 64; // this should be a minimum defined somewhere
2199 } else if (pos > region->last_frame()) {
2200 fade_length = region->length();
2202 fade_length = pos - region->position();
2205 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2207 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2213 tmp->reset_fade_in_shape_width (fade_length);
2214 tmp->show_fade_line((framecnt_t) fade_length);
2217 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2221 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2223 if (!movement_occurred) {
2227 framecnt_t fade_length;
2229 framepos_t const pos = adjusted_current_frame (event);
2231 boost::shared_ptr<Region> region = _primary->region ();
2233 if (pos < (region->position() + 64)) {
2234 fade_length = 64; // this should be a minimum defined somewhere
2235 } else if (pos > region->last_frame()) {
2236 fade_length = region->length();
2238 fade_length = pos - region->position();
2241 _editor->begin_reversible_command (_("change fade in length"));
2243 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2245 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2251 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2252 XMLNode &before = alist->get_state();
2254 tmp->audio_region()->set_fade_in_length (fade_length);
2255 tmp->audio_region()->set_fade_in_active (true);
2256 tmp->hide_fade_line();
2258 XMLNode &after = alist->get_state();
2259 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2262 _editor->commit_reversible_command ();
2266 FadeInDrag::aborted (bool)
2268 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2269 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2275 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2276 tmp->hide_fade_line();
2280 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2281 : RegionDrag (e, i, p, v)
2283 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2287 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2289 Drag::start_grab (event, cursor);
2291 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2292 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2294 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2296 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2300 FadeOutDrag::setup_pointer_frame_offset ()
2302 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2303 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2304 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2308 FadeOutDrag::motion (GdkEvent* event, bool)
2310 framecnt_t fade_length;
2312 framepos_t const pos = adjusted_current_frame (event);
2314 boost::shared_ptr<Region> region = _primary->region ();
2316 if (pos > (region->last_frame() - 64)) {
2317 fade_length = 64; // this should really be a minimum fade defined somewhere
2319 else if (pos < region->position()) {
2320 fade_length = region->length();
2323 fade_length = region->last_frame() - pos;
2326 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2328 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2334 tmp->reset_fade_out_shape_width (fade_length);
2335 tmp->show_fade_line(region->length() - fade_length);
2338 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2342 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2344 if (!movement_occurred) {
2348 framecnt_t fade_length;
2350 framepos_t const pos = adjusted_current_frame (event);
2352 boost::shared_ptr<Region> region = _primary->region ();
2354 if (pos > (region->last_frame() - 64)) {
2355 fade_length = 64; // this should really be a minimum fade defined somewhere
2357 else if (pos < region->position()) {
2358 fade_length = region->length();
2361 fade_length = region->last_frame() - pos;
2364 _editor->begin_reversible_command (_("change fade out length"));
2366 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2368 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2374 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2375 XMLNode &before = alist->get_state();
2377 tmp->audio_region()->set_fade_out_length (fade_length);
2378 tmp->audio_region()->set_fade_out_active (true);
2379 tmp->hide_fade_line();
2381 XMLNode &after = alist->get_state();
2382 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2385 _editor->commit_reversible_command ();
2389 FadeOutDrag::aborted (bool)
2391 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2392 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2398 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2399 tmp->hide_fade_line();
2403 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2406 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2408 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2411 _points.push_back (Gnome::Art::Point (0, 0));
2412 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2415 MarkerDrag::~MarkerDrag ()
2417 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2423 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2425 Drag::start_grab (event, cursor);
2429 Location *location = _editor->find_location_from_marker (_marker, is_start);
2430 _editor->_dragging_edit_point = true;
2432 update_item (location);
2434 // _drag_line->show();
2435 // _line->raise_to_top();
2438 show_verbose_cursor_time (location->start());
2440 show_verbose_cursor_time (location->end());
2443 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2446 case Selection::Toggle:
2447 _editor->selection->toggle (_marker);
2449 case Selection::Set:
2450 if (!_editor->selection->selected (_marker)) {
2451 _editor->selection->set (_marker);
2454 case Selection::Extend:
2456 Locations::LocationList ll;
2457 list<Marker*> to_add;
2459 _editor->selection->markers.range (s, e);
2460 s = min (_marker->position(), s);
2461 e = max (_marker->position(), e);
2464 if (e < max_framepos) {
2467 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2468 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2469 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2472 to_add.push_back (lm->start);
2475 to_add.push_back (lm->end);
2479 if (!to_add.empty()) {
2480 _editor->selection->add (to_add);
2484 case Selection::Add:
2485 _editor->selection->add (_marker);
2489 /* Set up copies for us to manipulate during the drag */
2491 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2492 Location* l = _editor->find_location_from_marker (*i, is_start);
2493 _copied_locations.push_back (new Location (*l));
2498 MarkerDrag::setup_pointer_frame_offset ()
2501 Location *location = _editor->find_location_from_marker (_marker, is_start);
2502 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2506 MarkerDrag::motion (GdkEvent* event, bool)
2508 framecnt_t f_delta = 0;
2510 bool move_both = false;
2512 Location *real_location;
2513 Location *copy_location = 0;
2515 framepos_t const newframe = adjusted_current_frame (event);
2517 framepos_t next = newframe;
2519 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2523 MarkerSelection::iterator i;
2524 list<Location*>::iterator x;
2526 /* find the marker we're dragging, and compute the delta */
2528 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2529 x != _copied_locations.end() && i != _editor->selection->markers.end();
2535 if (marker == _marker) {
2537 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2542 if (real_location->is_mark()) {
2543 f_delta = newframe - copy_location->start();
2547 switch (marker->type()) {
2548 case Marker::SessionStart:
2549 case Marker::RangeStart:
2550 case Marker::LoopStart:
2551 case Marker::PunchIn:
2552 f_delta = newframe - copy_location->start();
2555 case Marker::SessionEnd:
2556 case Marker::RangeEnd:
2557 case Marker::LoopEnd:
2558 case Marker::PunchOut:
2559 f_delta = newframe - copy_location->end();
2562 /* what kind of marker is this ? */
2570 if (i == _editor->selection->markers.end()) {
2571 /* hmm, impossible - we didn't find the dragged marker */
2575 /* now move them all */
2577 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2578 x != _copied_locations.end() && i != _editor->selection->markers.end();
2584 /* call this to find out if its the start or end */
2586 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2590 if (real_location->locked()) {
2594 if (copy_location->is_mark()) {
2598 copy_location->set_start (copy_location->start() + f_delta);
2602 framepos_t new_start = copy_location->start() + f_delta;
2603 framepos_t new_end = copy_location->end() + f_delta;
2605 if (is_start) { // start-of-range marker
2608 copy_location->set_start (new_start);
2609 copy_location->set_end (new_end);
2610 } else if (new_start < copy_location->end()) {
2611 copy_location->set_start (new_start);
2612 } else if (newframe > 0) {
2613 _editor->snap_to (next, 1, true);
2614 copy_location->set_end (next);
2615 copy_location->set_start (newframe);
2618 } else { // end marker
2621 copy_location->set_end (new_end);
2622 copy_location->set_start (new_start);
2623 } else if (new_end > copy_location->start()) {
2624 copy_location->set_end (new_end);
2625 } else if (newframe > 0) {
2626 _editor->snap_to (next, -1, true);
2627 copy_location->set_start (next);
2628 copy_location->set_end (newframe);
2633 update_item (copy_location);
2635 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2638 lm->set_position (copy_location->start(), copy_location->end());
2642 assert (!_copied_locations.empty());
2644 show_verbose_cursor_time (newframe);
2647 _editor->update_canvas_now ();
2652 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2654 if (!movement_occurred) {
2656 /* just a click, do nothing but finish
2657 off the selection process
2660 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2663 case Selection::Set:
2664 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2665 _editor->selection->set (_marker);
2669 case Selection::Toggle:
2670 case Selection::Extend:
2671 case Selection::Add:
2678 _editor->_dragging_edit_point = false;
2680 _editor->begin_reversible_command ( _("move marker") );
2681 XMLNode &before = _editor->session()->locations()->get_state();
2683 MarkerSelection::iterator i;
2684 list<Location*>::iterator x;
2687 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2688 x != _copied_locations.end() && i != _editor->selection->markers.end();
2691 Location * location = _editor->find_location_from_marker (*i, is_start);
2695 if (location->locked()) {
2699 if (location->is_mark()) {
2700 location->set_start ((*x)->start());
2702 location->set ((*x)->start(), (*x)->end());
2707 XMLNode &after = _editor->session()->locations()->get_state();
2708 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2709 _editor->commit_reversible_command ();
2713 MarkerDrag::aborted (bool)
2719 MarkerDrag::update_item (Location*)
2724 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2726 _cumulative_x_drag (0),
2727 _cumulative_y_drag (0)
2729 if (_zero_gain_fraction < 0.0) {
2730 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2733 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2735 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2741 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2743 Drag::start_grab (event, _editor->cursors()->fader);
2745 // start the grab at the center of the control point so
2746 // the point doesn't 'jump' to the mouse after the first drag
2747 _fixed_grab_x = _point->get_x();
2748 _fixed_grab_y = _point->get_y();
2750 float const fraction = 1 - (_point->get_y() / _point->line().height());
2752 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2754 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2755 event->button.x + 10, event->button.y + 10);
2757 _editor->verbose_cursor()->show ();
2761 ControlPointDrag::motion (GdkEvent* event, bool)
2763 double dx = _drags->current_pointer_x() - last_pointer_x();
2764 double dy = _drags->current_pointer_y() - last_pointer_y();
2766 if (event->button.state & Keyboard::SecondaryModifier) {
2771 /* coordinate in pixels relative to the start of the region (for region-based automation)
2772 or track (for track-based automation) */
2773 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2774 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2776 // calculate zero crossing point. back off by .01 to stay on the
2777 // positive side of zero
2778 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2780 // make sure we hit zero when passing through
2781 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2785 if (_x_constrained) {
2788 if (_y_constrained) {
2792 _cumulative_x_drag = cx - _fixed_grab_x;
2793 _cumulative_y_drag = cy - _fixed_grab_y;
2797 cy = min ((double) _point->line().height(), cy);
2799 framepos_t cx_frames = _editor->unit_to_frame (cx);
2801 if (!_x_constrained) {
2802 _editor->snap_to_with_modifier (cx_frames, event);
2805 cx_frames = min (cx_frames, _point->line().maximum_time());
2807 float const fraction = 1.0 - (cy / _point->line().height());
2809 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2811 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2813 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2817 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2819 if (!movement_occurred) {
2823 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2824 _editor->reset_point_selection ();
2828 motion (event, false);
2831 _point->line().end_drag ();
2832 _editor->session()->commit_reversible_command ();
2836 ControlPointDrag::aborted (bool)
2838 _point->line().reset ();
2842 ControlPointDrag::active (Editing::MouseMode m)
2844 if (m == Editing::MouseGain) {
2845 /* always active in mouse gain */
2849 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2850 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2853 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2856 _cumulative_y_drag (0)
2858 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2862 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2864 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2867 _item = &_line->grab_item ();
2869 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2870 origin, and ditto for y.
2873 double cx = event->button.x;
2874 double cy = event->button.y;
2876 _line->parent_group().w2i (cx, cy);
2878 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2883 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2884 /* no adjacent points */
2888 Drag::start_grab (event, _editor->cursors()->fader);
2890 /* store grab start in parent frame */
2895 double fraction = 1.0 - (cy / _line->height());
2897 _line->start_drag_line (before, after, fraction);
2899 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2900 event->button.x + 10, event->button.y + 10);
2902 _editor->verbose_cursor()->show ();
2906 LineDrag::motion (GdkEvent* event, bool)
2908 double dy = _drags->current_pointer_y() - last_pointer_y();
2910 if (event->button.state & Keyboard::SecondaryModifier) {
2914 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2916 _cumulative_y_drag = cy - _fixed_grab_y;
2919 cy = min ((double) _line->height(), cy);
2921 double const fraction = 1.0 - (cy / _line->height());
2925 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2931 /* we are ignoring x position for this drag, so we can just pass in anything */
2932 _line->drag_motion (0, fraction, true, push);
2934 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
2938 LineDrag::finished (GdkEvent* event, bool)
2940 motion (event, false);
2942 _editor->session()->commit_reversible_command ();
2946 LineDrag::aborted (bool)
2951 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2954 _cumulative_x_drag (0)
2956 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2960 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2962 Drag::start_grab (event);
2964 _line = reinterpret_cast<Line*> (_item);
2967 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2969 double cx = event->button.x;
2970 double cy = event->button.y;
2972 _item->property_parent().get_value()->w2i(cx, cy);
2974 /* store grab start in parent frame */
2975 _region_view_grab_x = cx;
2977 _before = *(float*) _item->get_data ("position");
2979 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2981 _max_x = _editor->frame_to_pixel(_arv->get_duration());
2985 FeatureLineDrag::motion (GdkEvent*, bool)
2987 double dx = _drags->current_pointer_x() - last_pointer_x();
2989 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2991 _cumulative_x_drag += dx;
2993 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3002 ArdourCanvas::Points points;
3004 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3006 _line->get_bounds(x1, y2, x2, y2);
3008 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3009 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3011 _line->property_points() = points;
3013 float *pos = new float;
3016 _line->set_data ("position", pos);
3022 FeatureLineDrag::finished (GdkEvent*, bool)
3024 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3025 _arv->update_transient(_before, _before);
3029 FeatureLineDrag::aborted (bool)
3034 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3037 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3041 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3043 Drag::start_grab (event);
3044 show_verbose_cursor_time (adjusted_current_frame (event));
3048 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3055 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3057 framepos_t grab = grab_frame ();
3058 if (Config->get_rubberbanding_snaps_to_grid ()) {
3059 _editor->snap_to_with_modifier (grab, event);
3062 /* base start and end on initial click position */
3072 if (_drags->current_pointer_y() < grab_y()) {
3073 y1 = _drags->current_pointer_y();
3076 y2 = _drags->current_pointer_y();
3081 if (start != end || y1 != y2) {
3083 double x1 = _editor->frame_to_pixel (start);
3084 double x2 = _editor->frame_to_pixel (end);
3086 _editor->rubberband_rect->property_x1() = x1;
3087 _editor->rubberband_rect->property_y1() = y1;
3088 _editor->rubberband_rect->property_x2() = x2;
3089 _editor->rubberband_rect->property_y2() = y2;
3091 _editor->rubberband_rect->show();
3092 _editor->rubberband_rect->raise_to_top();
3094 show_verbose_cursor_time (pf);
3099 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3101 if (movement_occurred) {
3103 motion (event, false);
3106 if (_drags->current_pointer_y() < grab_y()) {
3107 y1 = _drags->current_pointer_y();
3110 y2 = _drags->current_pointer_y();
3115 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3117 _editor->begin_reversible_command (_("rubberband selection"));
3119 if (grab_frame() < last_pointer_frame()) {
3120 _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3122 _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3125 _editor->commit_reversible_command ();
3128 if (!getenv("ARDOUR_SAE")) {
3129 _editor->selection->clear_tracks();
3131 _editor->selection->clear_regions();
3132 _editor->selection->clear_points ();
3133 _editor->selection->clear_lines ();
3136 _editor->rubberband_rect->hide();
3140 RubberbandSelectDrag::aborted (bool)
3142 _editor->rubberband_rect->hide ();
3145 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3146 : RegionDrag (e, i, p, v)
3148 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3152 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3154 Drag::start_grab (event, cursor);
3156 show_verbose_cursor_time (adjusted_current_frame (event));
3160 TimeFXDrag::motion (GdkEvent* event, bool)
3162 RegionView* rv = _primary;
3164 framepos_t const pf = adjusted_current_frame (event);
3166 if (pf > rv->region()->position()) {
3167 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3170 show_verbose_cursor_time (pf);
3174 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3176 _primary->get_time_axis_view().hide_timestretch ();
3178 if (!movement_occurred) {
3182 if (last_pointer_frame() < _primary->region()->position()) {
3183 /* backwards drag of the left edge - not usable */
3187 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3189 float percentage = (double) newlen / (double) _primary->region()->length();
3191 #ifndef USE_RUBBERBAND
3192 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3193 if (_primary->region()->data_type() == DataType::AUDIO) {
3194 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3198 // XXX how do timeFX on multiple regions ?
3203 if (_editor->time_stretch (rs, percentage) == -1) {
3204 error << _("An error occurred while executing time stretch operation") << endmsg;
3209 TimeFXDrag::aborted (bool)
3211 _primary->get_time_axis_view().hide_timestretch ();
3214 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3217 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3221 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3223 Drag::start_grab (event);
3227 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3229 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3233 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3235 if (movement_occurred && _editor->session()) {
3236 /* make sure we stop */
3237 _editor->session()->request_transport_speed (0.0);
3242 ScrubDrag::aborted (bool)
3247 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3251 , _original_pointer_time_axis (-1)
3252 , _last_pointer_time_axis (-1)
3254 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3258 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3260 if (_editor->session() == 0) {
3264 Gdk::Cursor* cursor = 0;
3266 switch (_operation) {
3267 case CreateSelection:
3268 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3273 cursor = _editor->cursors()->selector;
3274 Drag::start_grab (event, cursor);
3277 case SelectionStartTrim:
3278 if (_editor->clicked_axisview) {
3279 _editor->clicked_axisview->order_selection_trims (_item, true);
3281 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3284 case SelectionEndTrim:
3285 if (_editor->clicked_axisview) {
3286 _editor->clicked_axisview->order_selection_trims (_item, false);
3288 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3292 Drag::start_grab (event, cursor);
3296 if (_operation == SelectionMove) {
3297 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3299 show_verbose_cursor_time (adjusted_current_frame (event));
3302 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3306 SelectionDrag::setup_pointer_frame_offset ()
3308 switch (_operation) {
3309 case CreateSelection:
3310 _pointer_frame_offset = 0;
3313 case SelectionStartTrim:
3315 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3318 case SelectionEndTrim:
3319 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3325 SelectionDrag::motion (GdkEvent* event, bool first_move)
3327 framepos_t start = 0;
3331 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3332 if (pending_time_axis.first == 0) {
3336 framepos_t const pending_position = adjusted_current_frame (event);
3338 /* only alter selection if things have changed */
3340 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3344 switch (_operation) {
3345 case CreateSelection:
3347 framepos_t grab = grab_frame ();
3350 _editor->snap_to (grab);
3353 if (pending_position < grab_frame()) {
3354 start = pending_position;
3357 end = pending_position;
3361 /* first drag: Either add to the selection
3362 or create a new selection
3368 /* adding to the selection */
3369 _editor->set_selected_track_as_side_effect (Selection::Add);
3370 //_editor->selection->add (_editor->clicked_axisview);
3371 _editor->clicked_selection = _editor->selection->add (start, end);
3376 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3377 //_editor->selection->set (_editor->clicked_axisview);
3378 _editor->set_selected_track_as_side_effect (Selection::Set);
3381 _editor->clicked_selection = _editor->selection->set (start, end);
3385 /* select the track that we're in */
3386 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3387 // _editor->set_selected_track_as_side_effect (Selection::Add);
3388 _editor->selection->add (pending_time_axis.first);
3389 _added_time_axes.push_back (pending_time_axis.first);
3392 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3393 tracks that we selected in the first place.
3396 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3397 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3399 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3400 while (i != _added_time_axes.end()) {
3402 list<TimeAxisView*>::iterator tmp = i;
3405 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3406 _editor->selection->remove (*i);
3407 _added_time_axes.remove (*i);
3416 case SelectionStartTrim:
3418 start = _editor->selection->time[_editor->clicked_selection].start;
3419 end = _editor->selection->time[_editor->clicked_selection].end;
3421 if (pending_position > end) {
3424 start = pending_position;
3428 case SelectionEndTrim:
3430 start = _editor->selection->time[_editor->clicked_selection].start;
3431 end = _editor->selection->time[_editor->clicked_selection].end;
3433 if (pending_position < start) {
3436 end = pending_position;
3443 start = _editor->selection->time[_editor->clicked_selection].start;
3444 end = _editor->selection->time[_editor->clicked_selection].end;
3446 length = end - start;
3448 start = pending_position;
3449 _editor->snap_to (start);
3451 end = start + length;
3456 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3457 _editor->start_canvas_autoscroll (1, 0);
3461 _editor->selection->replace (_editor->clicked_selection, start, end);
3464 if (_operation == SelectionMove) {
3465 show_verbose_cursor_time(start);
3467 show_verbose_cursor_time(pending_position);
3472 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3474 Session* s = _editor->session();
3476 if (movement_occurred) {
3477 motion (event, false);
3478 /* XXX this is not object-oriented programming at all. ick */
3479 if (_editor->selection->time.consolidate()) {
3480 _editor->selection->TimeChanged ();
3483 /* XXX what if its a music time selection? */
3484 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3485 s->request_play_range (&_editor->selection->time, true);
3490 /* just a click, no pointer movement.*/
3492 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3493 _editor->selection->clear_time();
3496 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3497 _editor->selection->set (_editor->clicked_axisview);
3500 if (s && s->get_play_range () && s->transport_rolling()) {
3501 s->request_stop (false, false);
3506 _editor->stop_canvas_autoscroll ();
3510 SelectionDrag::aborted (bool)
3515 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3520 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3522 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3523 physical_screen_height (_editor->get_window()));
3524 _drag_rect->hide ();
3526 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3527 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3531 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3533 if (_editor->session() == 0) {
3537 Gdk::Cursor* cursor = 0;
3539 if (!_editor->temp_location) {
3540 _editor->temp_location = new Location (*_editor->session());
3543 switch (_operation) {
3544 case CreateRangeMarker:
3545 case CreateTransportMarker:
3546 case CreateCDMarker:
3548 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3553 cursor = _editor->cursors()->selector;
3557 Drag::start_grab (event, cursor);
3559 show_verbose_cursor_time (adjusted_current_frame (event));
3563 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3565 framepos_t start = 0;
3567 ArdourCanvas::SimpleRect *crect;
3569 switch (_operation) {
3570 case CreateRangeMarker:
3571 crect = _editor->range_bar_drag_rect;
3573 case CreateTransportMarker:
3574 crect = _editor->transport_bar_drag_rect;
3576 case CreateCDMarker:
3577 crect = _editor->cd_marker_bar_drag_rect;
3580 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3585 framepos_t const pf = adjusted_current_frame (event);
3587 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3588 framepos_t grab = grab_frame ();
3589 _editor->snap_to (grab);
3591 if (pf < grab_frame()) {
3599 /* first drag: Either add to the selection
3600 or create a new selection.
3605 _editor->temp_location->set (start, end);
3609 update_item (_editor->temp_location);
3611 //_drag_rect->raise_to_top();
3616 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3617 _editor->start_canvas_autoscroll (1, 0);
3621 _editor->temp_location->set (start, end);
3623 double x1 = _editor->frame_to_pixel (start);
3624 double x2 = _editor->frame_to_pixel (end);
3625 crect->property_x1() = x1;
3626 crect->property_x2() = x2;
3628 update_item (_editor->temp_location);
3631 show_verbose_cursor_time (pf);
3636 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3638 Location * newloc = 0;
3642 if (movement_occurred) {
3643 motion (event, false);
3646 switch (_operation) {
3647 case CreateRangeMarker:
3648 case CreateCDMarker:
3650 _editor->begin_reversible_command (_("new range marker"));
3651 XMLNode &before = _editor->session()->locations()->get_state();
3652 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3653 if (_operation == CreateCDMarker) {
3654 flags = Location::IsRangeMarker | Location::IsCDMarker;
3655 _editor->cd_marker_bar_drag_rect->hide();
3658 flags = Location::IsRangeMarker;
3659 _editor->range_bar_drag_rect->hide();
3661 newloc = new Location (
3662 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3665 _editor->session()->locations()->add (newloc, true);
3666 XMLNode &after = _editor->session()->locations()->get_state();
3667 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3668 _editor->commit_reversible_command ();
3672 case CreateTransportMarker:
3673 // popup menu to pick loop or punch
3674 _editor->new_transport_marker_context_menu (&event->button, _item);
3678 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3680 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3685 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3687 if (end == max_framepos) {
3688 end = _editor->session()->current_end_frame ();
3691 if (start == max_framepos) {
3692 start = _editor->session()->current_start_frame ();
3695 switch (_editor->mouse_mode) {
3697 /* find the two markers on either side and then make the selection from it */
3698 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3702 /* find the two markers on either side of the click and make the range out of it */
3703 _editor->selection->set (start, end);
3712 _editor->stop_canvas_autoscroll ();
3716 RangeMarkerBarDrag::aborted (bool)
3722 RangeMarkerBarDrag::update_item (Location* location)
3724 double const x1 = _editor->frame_to_pixel (location->start());
3725 double const x2 = _editor->frame_to_pixel (location->end());
3727 _drag_rect->property_x1() = x1;
3728 _drag_rect->property_x2() = x2;
3731 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3735 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3739 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3741 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3742 Drag::start_grab (event, _editor->cursors()->zoom_out);
3745 Drag::start_grab (event, _editor->cursors()->zoom_in);
3749 show_verbose_cursor_time (adjusted_current_frame (event));
3753 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3758 framepos_t const pf = adjusted_current_frame (event);
3760 framepos_t grab = grab_frame ();
3761 _editor->snap_to_with_modifier (grab, event);
3763 /* base start and end on initial click position */
3775 _editor->zoom_rect->show();
3776 _editor->zoom_rect->raise_to_top();
3779 _editor->reposition_zoom_rect(start, end);
3781 show_verbose_cursor_time (pf);
3786 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3788 if (movement_occurred) {
3789 motion (event, false);
3791 if (grab_frame() < last_pointer_frame()) {
3792 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3794 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3797 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3798 _editor->tav_zoom_step (_zoom_out);
3800 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3804 _editor->zoom_rect->hide();
3808 MouseZoomDrag::aborted (bool)
3810 _editor->zoom_rect->hide ();
3813 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3815 , _cumulative_dx (0)
3816 , _cumulative_dy (0)
3818 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3820 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3821 _region = &_primary->region_view ();
3822 _note_height = _region->midi_stream_view()->note_height ();
3826 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3828 Drag::start_grab (event);
3830 if (!(_was_selected = _primary->selected())) {
3832 /* tertiary-click means extend selection - we'll do that on button release,
3833 so don't add it here, because otherwise we make it hard to figure
3834 out the "extend-to" range.
3837 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3840 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3843 _region->note_selected (_primary, true);
3845 _region->unique_select (_primary);
3851 /** @return Current total drag x change in frames */
3853 NoteDrag::total_dx () const
3856 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3858 /* primary note time */
3859 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3861 /* new time of the primary note in session frames */
3862 frameoffset_t st = n + dx;
3864 framepos_t const rp = _region->region()->position ();
3866 /* prevent the note being dragged earlier than the region's position */
3869 /* snap and return corresponding delta */
3870 return _region->snap_frame_to_frame (st - rp) + rp - n;
3873 /** @return Current total drag y change in note number */
3875 NoteDrag::total_dy () const
3877 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3881 NoteDrag::motion (GdkEvent *, bool)
3883 /* Total change in x and y since the start of the drag */
3884 frameoffset_t const dx = total_dx ();
3885 int8_t const dy = total_dy ();
3887 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3888 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3889 double const tdy = -dy * _note_height - _cumulative_dy;
3892 _cumulative_dx += tdx;
3893 _cumulative_dy += tdy;
3895 int8_t note_delta = total_dy();
3897 _region->move_selection (tdx, tdy, note_delta);
3899 /* the new note value may be the same as the old one, but we
3900 * don't know what that means because the selection may have
3901 * involved more than one note and we might be doing something
3902 * odd with them. so show the note value anyway, always.
3906 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
3908 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
3909 (int) floor (new_note));
3911 show_verbose_cursor_text (buf);
3916 NoteDrag::finished (GdkEvent* ev, bool moved)
3919 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3921 if (_was_selected) {
3922 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3924 _region->note_deselected (_primary);
3927 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3928 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3930 if (!extend && !add && _region->selection_size() > 1) {
3931 _region->unique_select (_primary);
3932 } else if (extend) {
3933 _region->note_selected (_primary, true, true);
3935 /* it was added during button press */
3940 _region->note_dropped (_primary, total_dx(), total_dy());
3945 NoteDrag::aborted (bool)
3950 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3951 : Drag (editor, item)
3953 , _nothing_to_drag (false)
3955 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3957 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3960 /* get all lines in the automation view */
3961 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3963 /* find those that overlap the ranges being dragged */
3964 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3965 while (i != lines.end ()) {
3966 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3969 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3971 /* check this range against all the AudioRanges that we are using */
3972 list<AudioRange>::const_iterator k = _ranges.begin ();
3973 while (k != _ranges.end()) {
3974 if (k->coverage (r.first, r.second) != OverlapNone) {
3980 /* add it to our list if it overlaps at all */
3981 if (k != _ranges.end()) {
3986 _lines.push_back (n);
3992 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3996 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3998 Drag::start_grab (event, cursor);
4000 /* Get line states before we start changing things */
4001 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4002 i->state = &i->line->get_state ();
4005 if (_ranges.empty()) {
4007 /* No selected time ranges: drag all points */
4008 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4009 uint32_t const N = i->line->npoints ();
4010 for (uint32_t j = 0; j < N; ++j) {
4011 i->points.push_back (i->line->nth (j));
4017 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4019 framecnt_t const half = (i->start + i->end) / 2;
4021 /* find the line that this audio range starts in */
4022 list<Line>::iterator j = _lines.begin();
4023 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4027 if (j != _lines.end()) {
4028 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4030 /* j is the line that this audio range starts in; fade into it;
4031 64 samples length plucked out of thin air.
4034 framepos_t a = i->start + 64;
4039 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4040 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4042 the_list->add (p, the_list->eval (p));
4043 j->line->add_always_in_view (p);
4044 the_list->add (q, the_list->eval (q));
4045 j->line->add_always_in_view (q);
4048 /* same thing for the end */
4051 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4055 if (j != _lines.end()) {
4056 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4058 /* j is the line that this audio range starts in; fade out of it;
4059 64 samples length plucked out of thin air.
4062 framepos_t b = i->end - 64;
4067 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4068 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4070 the_list->add (p, the_list->eval (p));
4071 j->line->add_always_in_view (p);
4072 the_list->add (q, the_list->eval (q));
4073 j->line->add_always_in_view (q);
4077 _nothing_to_drag = true;
4079 /* Find all the points that should be dragged and put them in the relevant
4080 points lists in the Line structs.
4083 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4085 uint32_t const N = i->line->npoints ();
4086 for (uint32_t j = 0; j < N; ++j) {
4088 /* here's a control point on this line */
4089 ControlPoint* p = i->line->nth (j);
4090 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4092 /* see if it's inside a range */
4093 list<AudioRange>::const_iterator k = _ranges.begin ();
4094 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4098 if (k != _ranges.end()) {
4099 /* dragging this point */
4100 _nothing_to_drag = false;
4101 i->points.push_back (p);
4107 if (_nothing_to_drag) {
4111 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4112 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4117 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4119 if (_nothing_to_drag) {
4123 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4124 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4126 /* we are ignoring x position for this drag, so we can just pass in anything */
4127 i->line->drag_motion (0, f, true, false);
4132 AutomationRangeDrag::finished (GdkEvent* event, bool)
4134 if (_nothing_to_drag) {
4138 motion (event, false);
4139 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4140 i->line->end_drag ();
4141 i->line->clear_always_in_view ();
4144 _editor->session()->commit_reversible_command ();
4148 AutomationRangeDrag::aborted (bool)
4150 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4151 i->line->clear_always_in_view ();
4156 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4159 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4160 layer = v->region()->layer ();
4161 initial_y = v->get_canvas_group()->property_y ();
4162 initial_playlist = v->region()->playlist ();
4163 initial_position = v->region()->position ();
4164 initial_end = v->region()->position () + v->region()->length ();
4167 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4171 , _cumulative_dx (0)
4173 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4177 PatchChangeDrag::motion (GdkEvent* ev, bool)
4179 framepos_t f = adjusted_current_frame (ev);
4180 boost::shared_ptr<Region> r = _region_view->region ();
4181 f = max (f, r->position ());
4182 f = min (f, r->last_frame ());
4184 framecnt_t const dxf = f - grab_frame();
4185 double const dxu = _editor->frame_to_unit (dxf);
4186 _patch_change->move (dxu - _cumulative_dx, 0);
4187 _cumulative_dx = dxu;
4191 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4193 if (!movement_occurred) {
4197 boost::shared_ptr<Region> r (_region_view->region ());
4199 framepos_t f = adjusted_current_frame (ev);
4200 f = max (f, r->position ());
4201 f = min (f, r->last_frame ());
4203 _region_view->move_patch_change (
4205 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4210 PatchChangeDrag::aborted (bool)
4212 _patch_change->move (-_cumulative_dx, 0);
4216 PatchChangeDrag::setup_pointer_frame_offset ()
4218 boost::shared_ptr<Region> region = _region_view->region ();
4219 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());