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 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, time);
210 _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)
217 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
219 if (Keyboard::is_button2_event (&event->button)) {
220 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
221 _y_constrained = true;
222 _x_constrained = false;
224 _y_constrained = false;
225 _x_constrained = true;
228 _x_constrained = false;
229 _y_constrained = false;
232 _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
233 setup_pointer_frame_offset ();
234 _grab_frame = adjusted_frame (_raw_grab_frame, event);
235 _last_pointer_frame = _grab_frame;
236 _last_pointer_x = _grab_x;
237 _last_pointer_y = _grab_y;
240 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
243 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
248 if (_editor->session() && _editor->session()->transport_rolling()) {
251 _was_rolling = false;
254 switch (_editor->snap_type()) {
255 case SnapToRegionStart:
256 case SnapToRegionEnd:
257 case SnapToRegionSync:
258 case SnapToRegionBoundary:
259 _editor->build_region_boundary_cache ();
266 /** Call to end a drag `successfully'. Ungrabs item and calls
267 * subclass' finished() method.
269 * @param event GDK event, or 0.
270 * @return true if some movement occurred, otherwise false.
273 Drag::end_grab (GdkEvent* event)
275 _editor->stop_canvas_autoscroll ();
277 _item->ungrab (event ? event->button.time : 0);
279 finished (event, _move_threshold_passed);
281 _editor->verbose_cursor()->hide ();
283 return _move_threshold_passed;
287 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
291 if (f > _pointer_frame_offset) {
292 pos = f - _pointer_frame_offset;
296 _editor->snap_to_with_modifier (pos, event);
303 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
305 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
309 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
311 /* check to see if we have moved in any way that matters since the last motion event */
312 if (_move_threshold_passed &&
313 (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
314 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
318 pair<framecnt_t, int> const threshold = move_threshold ();
320 bool const old_move_threshold_passed = _move_threshold_passed;
322 if (!from_autoscroll && !_move_threshold_passed) {
324 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
325 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
327 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
330 if (active (_editor->mouse_mode) && _move_threshold_passed) {
332 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
333 if (!from_autoscroll) {
334 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
337 motion (event, _move_threshold_passed != old_move_threshold_passed);
339 _last_pointer_x = _drags->current_pointer_x ();
340 _last_pointer_y = _drags->current_pointer_y ();
341 _last_pointer_frame = adjusted_current_frame (event);
349 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
357 aborted (_move_threshold_passed);
359 _editor->stop_canvas_autoscroll ();
360 _editor->verbose_cursor()->hide ();
364 Drag::show_verbose_cursor_time (framepos_t frame)
366 _editor->verbose_cursor()->set_time (
368 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
369 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
372 _editor->verbose_cursor()->show ();
376 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
378 _editor->verbose_cursor()->show (xoffset);
380 _editor->verbose_cursor()->set_duration (
382 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
383 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
388 Drag::show_verbose_cursor_text (string const & text)
390 _editor->verbose_cursor()->show ();
392 _editor->verbose_cursor()->set (
394 _drags->current_pointer_x() + 10 - _editor->horizontal_position(),
395 _drags->current_pointer_y() + 10 - _editor->vertical_adjustment.get_value() + _editor->canvas_timebars_vsize
399 boost::shared_ptr<Region>
400 Drag::add_midi_region (MidiTimeAxisView* view)
402 if (_editor->session()) {
403 const TempoMap& map (_editor->session()->tempo_map());
404 framecnt_t pos = grab_frame();
405 const Meter& m = map.meter_at (pos);
406 /* not that the frame rate used here can be affected by pull up/down which
409 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
410 return view->add_region (grab_frame(), len, true);
413 return boost::shared_ptr<Region>();
416 struct EditorOrderTimeAxisViewSorter {
417 bool operator() (TimeAxisView* a, TimeAxisView* b) {
418 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
419 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
421 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
425 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
429 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
431 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
432 as some of the regions we are dragging may be on such tracks.
435 TrackViewList track_views = _editor->track_views;
436 track_views.sort (EditorOrderTimeAxisViewSorter ());
438 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
439 _time_axis_views.push_back (*i);
441 TimeAxisView::Children children_list = (*i)->get_child_list ();
442 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
443 _time_axis_views.push_back (j->get());
447 /* the list of views can be empty at this point if this is a region list-insert drag
450 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
451 _views.push_back (DraggingView (*i, this));
454 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
458 RegionDrag::region_going_away (RegionView* v)
460 list<DraggingView>::iterator i = _views.begin ();
461 while (i != _views.end() && i->view != v) {
465 if (i != _views.end()) {
470 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
471 * or -1 if it is not found.
474 RegionDrag::find_time_axis_view (TimeAxisView* t) const
477 int const N = _time_axis_views.size ();
478 while (i < N && _time_axis_views[i] != t) {
489 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
490 : RegionDrag (e, i, p, v),
499 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
501 Drag::start_grab (event, cursor);
503 show_verbose_cursor_time (_last_frame_position);
505 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
506 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
507 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
511 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
513 /* compute the amount of pointer motion in frames, and where
514 the region would be if we moved it by that much.
516 *pending_region_position = adjusted_current_frame (event);
518 framepos_t sync_frame;
519 framecnt_t sync_offset;
522 sync_offset = _primary->region()->sync_offset (sync_dir);
524 /* we don't handle a sync point that lies before zero.
526 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
528 sync_frame = *pending_region_position + (sync_dir*sync_offset);
530 _editor->snap_to_with_modifier (sync_frame, event);
532 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
535 *pending_region_position = _last_frame_position;
538 if (*pending_region_position > max_framepos - _primary->region()->length()) {
539 *pending_region_position = _last_frame_position;
544 /* in locked edit mode, reverse the usual meaning of _x_constrained */
545 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
547 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
549 /* x movement since last time (in pixels) */
550 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
552 /* total x movement */
553 framecnt_t total_dx = *pending_region_position;
554 if (regions_came_from_canvas()) {
555 total_dx = total_dx - grab_frame ();
558 /* check that no regions have gone off the start of the session */
559 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
560 if ((i->view->region()->position() + total_dx) < 0) {
562 *pending_region_position = _last_frame_position;
567 _last_frame_position = *pending_region_position;
574 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
576 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
577 int const n = i->time_axis_view + delta_track;
578 if (n < 0 || n >= int (_time_axis_views.size())) {
579 /* off the top or bottom track */
583 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
584 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
585 /* not a track, or the wrong type */
589 double const l = i->layer + delta_layer;
591 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
592 mode to allow the user to place a region below another on layer 0.
594 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
595 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
596 If it has, the layers will be munged later anyway, so it's ok.
602 /* all regions being dragged are ok with this change */
607 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
609 assert (!_views.empty ());
611 /* Find the TimeAxisView that the pointer is now over */
612 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
614 if (first_move && tv.first->view()->layer_display() == Stacked) {
615 tv.first->view()->set_layer_display (Expanded);
618 /* Bail early if we're not over a track */
619 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
620 if (!rtv || !rtv->is_track()) {
621 _editor->verbose_cursor()->hide ();
625 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
627 /* Here's the current pointer position in terms of time axis view and layer */
628 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
629 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
631 /* Work out the change in x */
632 framepos_t pending_region_position;
633 double const x_delta = compute_x_delta (event, &pending_region_position);
635 /* Work out the change in y */
636 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
637 double delta_layer = current_pointer_layer - _last_pointer_layer;
639 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
640 /* this y movement is not allowed, so do no y movement this time */
641 delta_time_axis_view = 0;
645 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
646 /* haven't reached next snap point, and we're not switching
647 trackviews nor layers. nothing to do.
652 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
654 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
656 RegionView* rv = i->view;
658 if (rv->region()->locked()) {
664 rv->get_time_axis_view().hide_dependent_views (*rv);
666 /* Absolutely no idea why this is necessary, but it is; without
667 it, the region view disappears after the reparent.
669 _editor->update_canvas_now ();
671 /* Reparent to a non scrolling group so that we can keep the
672 region selection above all time axis views.
673 Reparenting means that we will have to move the region view
674 later, as the two parent groups have different coordinates.
677 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
679 rv->fake_set_opaque (true);
682 /* If we have moved tracks, we'll fudge the layer delta so that the
683 region gets moved back onto layer 0 on its new track; this avoids
684 confusion when dragging regions from non-zero layers onto different
687 double this_delta_layer = delta_layer;
688 if (delta_time_axis_view != 0) {
689 this_delta_layer = - i->layer;
692 /* The TimeAxisView that this region is now on */
693 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
695 /* Ensure it is moved from stacked -> expanded if appropriate */
696 if (tv->view()->layer_display() == Stacked) {
697 tv->view()->set_layer_display (Expanded);
700 /* We're only allowed to go -ve in layer on Expanded views */
701 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
702 this_delta_layer = - i->layer;
706 rv->set_height (tv->view()->child_height ());
708 /* Update show/hidden status as the region view may have come from a hidden track,
709 or have moved to one.
712 rv->get_canvas_group()->hide ();
714 rv->get_canvas_group()->show ();
717 /* Update the DraggingView */
718 i->time_axis_view += delta_time_axis_view;
719 i->layer += this_delta_layer;
722 _editor->mouse_brush_insert_region (rv, pending_region_position);
727 /* Get the y coordinate of the top of the track that this region is now on */
728 tv->canvas_display()->i2w (x, y);
729 y += _editor->get_trackview_group_vertical_offset();
731 /* And adjust for the layer that it should be on */
732 StreamView* cv = tv->view ();
733 switch (cv->layer_display ()) {
737 y += (cv->layers() - i->layer - 1) * cv->child_height ();
740 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
744 /* Now move the region view */
745 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
748 } /* foreach region */
750 _total_x_delta += x_delta;
753 _editor->cursor_group->raise_to_top();
756 if (x_delta != 0 && !_brushing) {
757 show_verbose_cursor_time (_last_frame_position);
760 _last_pointer_time_axis_view += delta_time_axis_view;
761 _last_pointer_layer += delta_layer;
765 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
767 if (_copy && first_move) {
769 /* duplicate the regionview(s) and region(s) */
771 list<DraggingView> new_regionviews;
773 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
775 RegionView* rv = i->view;
776 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
777 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
779 const boost::shared_ptr<const Region> original = rv->region();
780 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
781 region_copy->set_position (original->position());
785 boost::shared_ptr<AudioRegion> audioregion_copy
786 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
788 nrv = new AudioRegionView (*arv, audioregion_copy);
790 boost::shared_ptr<MidiRegion> midiregion_copy
791 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
792 nrv = new MidiRegionView (*mrv, midiregion_copy);
797 nrv->get_canvas_group()->show ();
798 new_regionviews.push_back (DraggingView (nrv, this));
800 /* swap _primary to the copy */
802 if (rv == _primary) {
806 /* ..and deselect the one we copied */
808 rv->set_selected (false);
811 if (!new_regionviews.empty()) {
813 /* reflect the fact that we are dragging the copies */
815 _views = new_regionviews;
817 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
820 sync the canvas to what we think is its current state
821 without it, the canvas seems to
822 "forget" to update properly after the upcoming reparent()
823 ..only if the mouse is in rapid motion at the time of the grab.
824 something to do with regionview creation taking so long?
826 _editor->update_canvas_now();
830 RegionMotionDrag::motion (event, first_move);
834 RegionMotionDrag::finished (GdkEvent *, bool)
836 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
841 if ((*i)->view()->layer_display() == Expanded) {
842 (*i)->view()->set_layer_display (Stacked);
848 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
850 RegionMotionDrag::finished (ev, movement_occurred);
852 if (!movement_occurred) {
857 /* reverse this here so that we have the correct logic to finalize
861 if (Config->get_edit_mode() == Lock) {
862 _x_constrained = !_x_constrained;
865 assert (!_views.empty ());
867 /* We might have hidden region views so that they weren't visible during the drag
868 (when they have been reparented). Now everything can be shown again, as region
869 views are back in their track parent groups.
871 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
872 i->view->get_canvas_group()->show ();
875 bool const changed_position = (_last_frame_position != _primary->region()->position());
876 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
877 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
879 _editor->update_canvas_now ();
901 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
903 RegionSelection new_views;
904 PlaylistSet modified_playlists;
905 list<RegionView*> views_to_delete;
908 /* all changes were made during motion event handlers */
910 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
914 _editor->commit_reversible_command ();
918 if (_x_constrained) {
919 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
921 _editor->begin_reversible_command (Operations::region_copy);
924 /* insert the regions into their new playlists */
925 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
927 if (i->view->region()->locked()) {
933 if (changed_position && !_x_constrained) {
934 where = i->view->region()->position() - drag_delta;
936 where = i->view->region()->position();
939 RegionView* new_view = insert_region_into_playlist (
940 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
947 new_views.push_back (new_view);
949 /* we don't need the copied RegionView any more */
950 views_to_delete.push_back (i->view);
953 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
954 because when views are deleted they are automagically removed from _views, which messes
957 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
961 /* If we've created new regions either by copying or moving
962 to a new track, we want to replace the old selection with the new ones
965 if (new_views.size() > 0) {
966 _editor->selection->set (new_views);
969 /* write commands for the accumulated diffs for all our modified playlists */
970 add_stateful_diff_commands_for_playlists (modified_playlists);
972 _editor->commit_reversible_command ();
976 RegionMoveDrag::finished_no_copy (
977 bool const changed_position,
978 bool const changed_tracks,
979 framecnt_t const drag_delta
982 RegionSelection new_views;
983 PlaylistSet modified_playlists;
984 PlaylistSet frozen_playlists;
985 set<RouteTimeAxisView*> views_to_update;
988 /* all changes were made during motion event handlers */
989 _editor->commit_reversible_command ();
993 if (_x_constrained) {
994 _editor->begin_reversible_command (_("fixed time region drag"));
996 _editor->begin_reversible_command (Operations::region_drag);
999 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1001 RegionView* rv = i->view;
1003 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1004 double const dest_layer = i->layer;
1006 if (rv->region()->locked()) {
1011 views_to_update.insert (dest_rtv);
1015 if (changed_position && !_x_constrained) {
1016 where = rv->region()->position() - drag_delta;
1018 where = rv->region()->position();
1021 if (changed_tracks) {
1023 /* insert into new playlist */
1025 RegionView* new_view = insert_region_into_playlist (
1026 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1029 if (new_view == 0) {
1034 new_views.push_back (new_view);
1036 /* remove from old playlist */
1038 /* the region that used to be in the old playlist is not
1039 moved to the new one - we use a copy of it. as a result,
1040 any existing editor for the region should no longer be
1043 rv->hide_region_editor();
1044 rv->fake_set_opaque (false);
1046 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1050 rv->region()->clear_changes ();
1053 motion on the same track. plonk the previously reparented region
1054 back to its original canvas group (its streamview).
1055 No need to do anything for copies as they are fake regions which will be deleted.
1058 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1059 rv->get_canvas_group()->property_y() = i->initial_y;
1060 rv->get_time_axis_view().reveal_dependent_views (*rv);
1062 /* just change the model */
1064 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1066 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1067 playlist->set_layer (rv->region(), dest_layer);
1070 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1072 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1075 playlist->freeze ();
1078 /* this movement may result in a crossfade being modified, so we need to get undo
1079 data from the playlist as well as the region.
1082 r = modified_playlists.insert (playlist);
1084 playlist->clear_changes ();
1087 rv->region()->set_position (where);
1089 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1092 if (changed_tracks) {
1094 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1095 was selected in all of them, then removing it from a playlist will have removed all
1096 trace of it from _views (i.e. there were N regions selected, we removed 1,
1097 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1098 corresponding regionview, and _views is now empty).
1100 This could have invalidated any and all iterators into _views.
1102 The heuristic we use here is: if the region selection is empty, break out of the loop
1103 here. if the region selection is not empty, then restart the loop because we know that
1104 we must have removed at least the region(view) we've just been working on as well as any
1105 that we processed on previous iterations.
1107 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1108 we can just iterate.
1112 if (_views.empty()) {
1123 /* If we've created new regions either by copying or moving
1124 to a new track, we want to replace the old selection with the new ones
1127 if (new_views.size() > 0) {
1128 _editor->selection->set (new_views);
1131 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1135 /* write commands for the accumulated diffs for all our modified playlists */
1136 add_stateful_diff_commands_for_playlists (modified_playlists);
1138 _editor->commit_reversible_command ();
1140 /* We have futzed with the layering of canvas items on our streamviews.
1141 If any region changed layer, this will have resulted in the stream
1142 views being asked to set up their region views, and all will be well.
1143 If not, we might now have badly-ordered region views. Ask the StreamViews
1144 involved to sort themselves out, just in case.
1147 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1148 (*i)->view()->playlist_layered ((*i)->track ());
1152 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1153 * @param region Region to remove.
1154 * @param playlist playlist To remove from.
1155 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1156 * that clear_changes () is only called once per playlist.
1159 RegionMoveDrag::remove_region_from_playlist (
1160 boost::shared_ptr<Region> region,
1161 boost::shared_ptr<Playlist> playlist,
1162 PlaylistSet& modified_playlists
1165 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1168 playlist->clear_changes ();
1171 playlist->remove_region (region);
1175 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1176 * clearing the playlist's diff history first if necessary.
1177 * @param region Region to insert.
1178 * @param dest_rtv Destination RouteTimeAxisView.
1179 * @param dest_layer Destination layer.
1180 * @param where Destination position.
1181 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1182 * that clear_changes () is only called once per playlist.
1183 * @return New RegionView, or 0 if no insert was performed.
1186 RegionMoveDrag::insert_region_into_playlist (
1187 boost::shared_ptr<Region> region,
1188 RouteTimeAxisView* dest_rtv,
1191 PlaylistSet& modified_playlists
1194 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1195 if (!dest_playlist) {
1199 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1200 _new_region_view = 0;
1201 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1203 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1204 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1206 dest_playlist->clear_changes ();
1209 dest_playlist->add_region (region, where);
1211 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1212 dest_playlist->set_layer (region, dest_layer);
1217 assert (_new_region_view);
1219 return _new_region_view;
1223 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1225 _new_region_view = rv;
1229 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1231 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1232 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1234 _editor->session()->add_command (c);
1243 RegionMoveDrag::aborted (bool movement_occurred)
1247 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1254 RegionMotionDrag::aborted (movement_occurred);
1259 RegionMotionDrag::aborted (bool)
1261 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1262 if ((*i)->view()->layer_display() == Expanded) {
1263 (*i)->view()->set_layer_display (Stacked);
1267 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1268 RegionView* rv = i->view;
1269 TimeAxisView* tv = &(rv->get_time_axis_view ());
1270 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1272 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1273 rv->get_canvas_group()->property_y() = 0;
1274 rv->get_time_axis_view().reveal_dependent_views (*rv);
1275 rv->fake_set_opaque (false);
1276 rv->move (-_total_x_delta, 0);
1277 rv->set_height (rtv->view()->child_height ());
1280 _editor->update_canvas_now ();
1283 /** @param b true to brush, otherwise false.
1284 * @param c true to make copies of the regions being moved, otherwise false.
1286 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1287 : RegionMotionDrag (e, i, p, v, b),
1290 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1293 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1294 if (rtv && rtv->is_track()) {
1295 speed = rtv->track()->speed ();
1298 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1302 RegionMoveDrag::setup_pointer_frame_offset ()
1304 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1307 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1308 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1310 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1312 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1313 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1315 _primary = v->view()->create_region_view (r, false, false);
1317 _primary->get_canvas_group()->show ();
1318 _primary->set_position (pos, 0);
1319 _views.push_back (DraggingView (_primary, this));
1321 _last_frame_position = pos;
1323 _item = _primary->get_canvas_group ();
1327 RegionInsertDrag::finished (GdkEvent *, bool)
1329 _editor->update_canvas_now ();
1331 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1333 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1334 _primary->get_canvas_group()->property_y() = 0;
1336 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1338 _editor->begin_reversible_command (Operations::insert_region);
1339 playlist->clear_changes ();
1340 playlist->add_region (_primary->region (), _last_frame_position);
1341 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1342 _editor->commit_reversible_command ();
1350 RegionInsertDrag::aborted (bool)
1357 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1358 : RegionMoveDrag (e, i, p, v, false, false)
1360 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1363 struct RegionSelectionByPosition {
1364 bool operator() (RegionView*a, RegionView* b) {
1365 return a->region()->position () < b->region()->position();
1370 RegionSpliceDrag::motion (GdkEvent* event, bool)
1372 /* Which trackview is this ? */
1374 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1375 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1377 /* The region motion is only processed if the pointer is over
1381 if (!tv || !tv->is_track()) {
1382 /* To make sure we hide the verbose canvas cursor when the mouse is
1383 not held over and audiotrack.
1385 _editor->verbose_cursor()->hide ();
1391 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1397 RegionSelection copy (_editor->selection->regions);
1399 RegionSelectionByPosition cmp;
1402 framepos_t const pf = adjusted_current_frame (event);
1404 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1406 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1412 boost::shared_ptr<Playlist> playlist;
1414 if ((playlist = atv->playlist()) == 0) {
1418 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1423 if (pf < (*i)->region()->last_frame() + 1) {
1427 if (pf > (*i)->region()->first_frame()) {
1433 playlist->shuffle ((*i)->region(), dir);
1438 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1440 RegionMoveDrag::finished (event, movement_occurred);
1444 RegionSpliceDrag::aborted (bool)
1449 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1451 _view (dynamic_cast<MidiTimeAxisView*> (v))
1453 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1459 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1462 _region = add_midi_region (_view);
1463 _view->playlist()->freeze ();
1466 framepos_t const f = adjusted_current_frame (event);
1467 if (f < grab_frame()) {
1468 _region->set_position (f);
1471 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1472 so that if this region is duplicated, its duplicate starts on
1473 a snap point rather than 1 frame after a snap point. Otherwise things get
1474 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1475 place snapped notes at the start of the region.
1478 framecnt_t const len = abs (f - grab_frame () - 1);
1479 _region->set_length (len < 1 ? 1 : len);
1485 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1487 if (!movement_occurred) {
1488 add_midi_region (_view);
1490 _view->playlist()->thaw ();
1494 _editor->commit_reversible_command ();
1499 RegionCreateDrag::aborted (bool)
1502 _view->playlist()->thaw ();
1508 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1512 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1516 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1518 Gdk::Cursor* cursor;
1519 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1520 float x_fraction = cnote->mouse_x_fraction ();
1522 if (x_fraction > 0.0 && x_fraction < 0.25) {
1523 cursor = _editor->cursors()->left_side_trim;
1525 cursor = _editor->cursors()->right_side_trim;
1528 Drag::start_grab (event, cursor);
1530 region = &cnote->region_view();
1532 double const region_start = region->get_position_pixels();
1533 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1535 if (grab_x() <= middle_point) {
1536 cursor = _editor->cursors()->left_side_trim;
1539 cursor = _editor->cursors()->right_side_trim;
1543 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1545 if (event->motion.state & Keyboard::PrimaryModifier) {
1551 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1553 if (ms.size() > 1) {
1554 /* has to be relative, may make no sense otherwise */
1558 /* select this note; if it is already selected, preserve the existing selection,
1559 otherwise make this note the only one selected.
1561 region->note_selected (cnote, cnote->selected ());
1563 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1564 MidiRegionSelection::iterator next;
1567 (*r)->begin_resizing (at_front);
1573 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1575 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1576 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1577 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1582 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1584 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1585 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1586 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1591 NoteResizeDrag::aborted (bool)
1593 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1594 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1595 (*r)->abort_resizing ();
1599 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1602 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1606 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1612 RegionGainDrag::finished (GdkEvent *, bool)
1618 RegionGainDrag::aborted (bool)
1623 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1624 : RegionDrag (e, i, p, v)
1626 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1630 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1633 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1634 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1636 if (tv && tv->is_track()) {
1637 speed = tv->track()->speed();
1640 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1641 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1642 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1644 framepos_t const pf = adjusted_current_frame (event);
1646 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1647 /* Move the contents of the region around without changing the region bounds */
1648 _operation = ContentsTrim;
1649 Drag::start_grab (event, _editor->cursors()->trimmer);
1651 /* These will get overridden for a point trim.*/
1652 if (pf < (region_start + region_length/2)) {
1653 /* closer to front */
1654 _operation = StartTrim;
1655 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1658 _operation = EndTrim;
1659 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1663 switch (_operation) {
1665 show_verbose_cursor_time (region_start);
1666 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1667 i->view->trim_front_starting ();
1671 show_verbose_cursor_time (region_end);
1674 show_verbose_cursor_time (pf);
1678 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1679 i->view->region()->suspend_property_changes ();
1684 TrimDrag::motion (GdkEvent* event, bool first_move)
1686 RegionView* rv = _primary;
1689 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1690 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1691 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1693 if (tv && tv->is_track()) {
1694 speed = tv->track()->speed();
1697 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1703 switch (_operation) {
1705 trim_type = "Region start trim";
1708 trim_type = "Region end trim";
1711 trim_type = "Region content trim";
1715 _editor->begin_reversible_command (trim_type);
1717 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1718 RegionView* rv = i->view;
1719 rv->fake_set_opaque (false);
1720 rv->enable_display (false);
1721 rv->region()->playlist()->clear_owned_changes ();
1723 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1726 arv->temporarily_hide_envelope ();
1729 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1730 insert_result = _editor->motion_frozen_playlists.insert (pl);
1732 if (insert_result.second) {
1738 bool non_overlap_trim = false;
1740 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1741 non_overlap_trim = true;
1744 switch (_operation) {
1746 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1747 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1752 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1753 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1759 bool swap_direction = false;
1761 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1762 swap_direction = true;
1765 framecnt_t frame_delta = 0;
1767 bool left_direction = false;
1768 if (last_pointer_frame() > adjusted_current_frame(event)) {
1769 left_direction = true;
1772 if (left_direction) {
1773 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1775 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1778 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1779 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1785 switch (_operation) {
1787 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1790 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1793 show_verbose_cursor_time (adjusted_current_frame (event));
1800 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1802 if (movement_occurred) {
1803 motion (event, false);
1805 /* This must happen before the region's StatefulDiffCommand is created, as it may
1806 `correct' (ahem) the region's _start from being negative to being zero. It
1807 needs to be zero in the undo record.
1809 if (_operation == StartTrim) {
1810 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1811 i->view->trim_front_ending ();
1815 if (!_editor->selection->selected (_primary)) {
1816 _primary->thaw_after_trim ();
1819 set<boost::shared_ptr<Playlist> > diffed_playlists;
1821 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1822 i->view->thaw_after_trim ();
1823 i->view->enable_display (true);
1824 i->view->fake_set_opaque (true);
1826 /* Trimming one region may affect others on the playlist, so we need
1827 to get undo Commands from the whole playlist rather than just the
1828 region. Use diffed_playlists to make sure we don't diff a given
1829 playlist more than once.
1831 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1832 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1833 vector<Command*> cmds;
1835 _editor->session()->add_commands (cmds);
1836 diffed_playlists.insert (p);
1840 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1844 _editor->motion_frozen_playlists.clear ();
1845 _editor->commit_reversible_command();
1848 /* no mouse movement */
1849 _editor->point_trim (event, adjusted_current_frame (event));
1852 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1853 if (_operation == StartTrim) {
1854 i->view->trim_front_ending ();
1857 i->view->region()->resume_property_changes ();
1862 TrimDrag::aborted (bool movement_occurred)
1864 /* Our motion method is changing model state, so use the Undo system
1865 to cancel. Perhaps not ideal, as this will leave an Undo point
1866 behind which may be slightly odd from the user's point of view.
1871 if (movement_occurred) {
1875 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1876 i->view->region()->resume_property_changes ();
1881 TrimDrag::setup_pointer_frame_offset ()
1883 list<DraggingView>::iterator i = _views.begin ();
1884 while (i != _views.end() && i->view != _primary) {
1888 if (i == _views.end()) {
1892 switch (_operation) {
1894 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1897 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1904 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1908 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1909 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1914 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1916 Drag::start_grab (event, cursor);
1917 show_verbose_cursor_time (adjusted_current_frame(event));
1921 MeterMarkerDrag::setup_pointer_frame_offset ()
1923 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1927 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1931 // create a dummy marker for visual representation of moving the
1932 // section, because whether its a copy or not, we're going to
1933 // leave or lose the original marker (leave if its a copy; lose if its
1934 // not, because we'll remove it from the map).
1936 MeterSection section (_marker->meter());
1938 if (!section.movable()) {
1943 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1945 _marker = new MeterMarker (
1947 *_editor->meter_group,
1948 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1950 *new MeterSection (_marker->meter())
1953 /* use the new marker for the grab */
1954 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1957 TempoMap& map (_editor->session()->tempo_map());
1958 /* get current state */
1959 before_state = &map.get_state();
1960 /* remove the section while we drag it */
1961 map.remove_meter (section, true);
1965 framepos_t const pf = adjusted_current_frame (event);
1966 _marker->set_position (pf);
1967 show_verbose_cursor_time (pf);
1971 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1973 if (!movement_occurred) {
1977 motion (event, false);
1979 Timecode::BBT_Time when;
1981 TempoMap& map (_editor->session()->tempo_map());
1982 map.bbt_time (last_pointer_frame(), when);
1984 if (_copy == true) {
1985 _editor->begin_reversible_command (_("copy meter mark"));
1986 XMLNode &before = map.get_state();
1987 map.add_meter (_marker->meter(), when);
1988 XMLNode &after = map.get_state();
1989 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1990 _editor->commit_reversible_command ();
1993 _editor->begin_reversible_command (_("move meter mark"));
1995 /* we removed it before, so add it back now */
1997 map.add_meter (_marker->meter(), when);
1998 XMLNode &after = map.get_state();
1999 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2000 _editor->commit_reversible_command ();
2003 // delete the dummy marker we used for visual representation while moving.
2004 // a new visual marker will show up automatically.
2009 MeterMarkerDrag::aborted (bool moved)
2011 _marker->set_position (_marker->meter().frame ());
2014 TempoMap& map (_editor->session()->tempo_map());
2015 /* we removed it before, so add it back now */
2016 map.add_meter (_marker->meter(), _marker->meter().frame());
2017 // delete the dummy marker we used for visual representation while moving.
2018 // a new visual marker will show up automatically.
2023 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2027 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2029 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2034 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2036 Drag::start_grab (event, cursor);
2037 show_verbose_cursor_time (adjusted_current_frame (event));
2041 TempoMarkerDrag::setup_pointer_frame_offset ()
2043 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2047 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2051 // create a dummy marker for visual representation of moving the
2052 // section, because whether its a copy or not, we're going to
2053 // leave or lose the original marker (leave if its a copy; lose if its
2054 // not, because we'll remove it from the map).
2056 // create a dummy marker for visual representation of moving the copy.
2057 // The actual copying is not done before we reach the finish callback.
2060 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2062 TempoSection section (_marker->tempo());
2064 _marker = new TempoMarker (
2066 *_editor->tempo_group,
2067 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2069 *new TempoSection (_marker->tempo())
2072 /* use the new marker for the grab */
2073 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2076 TempoMap& map (_editor->session()->tempo_map());
2077 /* get current state */
2078 before_state = &map.get_state();
2079 /* remove the section while we drag it */
2080 map.remove_tempo (section, true);
2084 framepos_t const pf = adjusted_current_frame (event);
2085 _marker->set_position (pf);
2086 show_verbose_cursor_time (pf);
2090 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2092 if (!movement_occurred) {
2096 motion (event, false);
2098 TempoMap& map (_editor->session()->tempo_map());
2099 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2100 Timecode::BBT_Time when;
2102 map.bbt_time (beat_time, when);
2104 if (_copy == true) {
2105 _editor->begin_reversible_command (_("copy tempo mark"));
2106 XMLNode &before = map.get_state();
2107 map.add_tempo (_marker->tempo(), when);
2108 XMLNode &after = map.get_state();
2109 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2110 _editor->commit_reversible_command ();
2113 _editor->begin_reversible_command (_("move tempo mark"));
2114 /* we removed it before, so add it back now */
2115 map.add_tempo (_marker->tempo(), when);
2116 XMLNode &after = map.get_state();
2117 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2118 _editor->commit_reversible_command ();
2121 // delete the dummy marker we used for visual representation while moving.
2122 // a new visual marker will show up automatically.
2127 TempoMarkerDrag::aborted (bool moved)
2129 _marker->set_position (_marker->tempo().frame());
2131 TempoMap& map (_editor->session()->tempo_map());
2132 /* we removed it before, so add it back now */
2133 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2134 // delete the dummy marker we used for visual representation while moving.
2135 // a new visual marker will show up automatically.
2140 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2144 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2147 /** Do all the things we do when dragging the playhead to make it look as though
2148 * we have located, without actually doing the locate (because that would cause
2149 * the diskstream buffers to be refilled, which is too slow).
2152 CursorDrag::fake_locate (framepos_t t)
2154 _editor->playhead_cursor->set_position (t);
2156 Session* s = _editor->session ();
2157 if (s->timecode_transmission_suspended ()) {
2158 framepos_t const f = _editor->playhead_cursor->current_frame;
2159 s->send_mmc_locate (f);
2160 s->send_full_time_code (f);
2163 show_verbose_cursor_time (t);
2164 _editor->UpdateAllTransportClocks (t);
2168 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2170 Drag::start_grab (event, c);
2172 _grab_zoom = _editor->frames_per_unit;
2174 framepos_t where = _editor->event_frame (event, 0, 0);
2175 _editor->snap_to_with_modifier (where, event);
2177 _editor->_dragging_playhead = true;
2179 Session* s = _editor->session ();
2182 if (_was_rolling && _stop) {
2186 if (s->is_auditioning()) {
2187 s->cancel_audition ();
2190 s->request_suspend_timecode_transmission ();
2191 while (!s->timecode_transmission_suspended ()) {
2192 /* twiddle our thumbs */
2196 fake_locate (where);
2200 CursorDrag::motion (GdkEvent* event, bool)
2202 framepos_t const adjusted_frame = adjusted_current_frame (event);
2203 if (adjusted_frame != last_pointer_frame()) {
2204 fake_locate (adjusted_frame);
2206 _editor->update_canvas_now ();
2212 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2214 _editor->_dragging_playhead = false;
2216 if (!movement_occurred && _stop) {
2220 motion (event, false);
2222 Session* s = _editor->session ();
2224 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2225 _editor->_pending_locate_request = true;
2226 s->request_resume_timecode_transmission ();
2231 CursorDrag::aborted (bool)
2233 if (_editor->_dragging_playhead) {
2234 _editor->session()->request_resume_timecode_transmission ();
2235 _editor->_dragging_playhead = false;
2238 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2241 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2242 : RegionDrag (e, i, p, v)
2244 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2248 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2250 Drag::start_grab (event, cursor);
2252 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2253 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2255 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2257 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2261 FadeInDrag::setup_pointer_frame_offset ()
2263 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2264 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2265 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2269 FadeInDrag::motion (GdkEvent* event, bool)
2271 framecnt_t fade_length;
2273 framepos_t const pos = adjusted_current_frame (event);
2275 boost::shared_ptr<Region> region = _primary->region ();
2277 if (pos < (region->position() + 64)) {
2278 fade_length = 64; // this should be a minimum defined somewhere
2279 } else if (pos > region->last_frame()) {
2280 fade_length = region->length();
2282 fade_length = pos - region->position();
2285 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2287 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2293 tmp->reset_fade_in_shape_width (fade_length);
2294 tmp->show_fade_line((framecnt_t) fade_length);
2297 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2301 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2303 if (!movement_occurred) {
2307 framecnt_t fade_length;
2309 framepos_t const pos = adjusted_current_frame (event);
2311 boost::shared_ptr<Region> region = _primary->region ();
2313 if (pos < (region->position() + 64)) {
2314 fade_length = 64; // this should be a minimum defined somewhere
2315 } else if (pos > region->last_frame()) {
2316 fade_length = region->length();
2318 fade_length = pos - region->position();
2321 _editor->begin_reversible_command (_("change fade in length"));
2323 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2325 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2331 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2332 XMLNode &before = alist->get_state();
2334 tmp->audio_region()->set_fade_in_length (fade_length);
2335 tmp->audio_region()->set_fade_in_active (true);
2336 tmp->hide_fade_line();
2338 XMLNode &after = alist->get_state();
2339 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2342 _editor->commit_reversible_command ();
2346 FadeInDrag::aborted (bool)
2348 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2349 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2355 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2356 tmp->hide_fade_line();
2360 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2361 : RegionDrag (e, i, p, v)
2363 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2367 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2369 Drag::start_grab (event, cursor);
2371 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2372 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2374 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2376 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2380 FadeOutDrag::setup_pointer_frame_offset ()
2382 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2383 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2384 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2388 FadeOutDrag::motion (GdkEvent* event, bool)
2390 framecnt_t fade_length;
2392 framepos_t const pos = adjusted_current_frame (event);
2394 boost::shared_ptr<Region> region = _primary->region ();
2396 if (pos > (region->last_frame() - 64)) {
2397 fade_length = 64; // this should really be a minimum fade defined somewhere
2399 else if (pos < region->position()) {
2400 fade_length = region->length();
2403 fade_length = region->last_frame() - pos;
2406 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2408 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2414 tmp->reset_fade_out_shape_width (fade_length);
2415 tmp->show_fade_line(region->length() - fade_length);
2418 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2422 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2424 if (!movement_occurred) {
2428 framecnt_t fade_length;
2430 framepos_t const pos = adjusted_current_frame (event);
2432 boost::shared_ptr<Region> region = _primary->region ();
2434 if (pos > (region->last_frame() - 64)) {
2435 fade_length = 64; // this should really be a minimum fade defined somewhere
2437 else if (pos < region->position()) {
2438 fade_length = region->length();
2441 fade_length = region->last_frame() - pos;
2444 _editor->begin_reversible_command (_("change fade out length"));
2446 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2448 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2454 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2455 XMLNode &before = alist->get_state();
2457 tmp->audio_region()->set_fade_out_length (fade_length);
2458 tmp->audio_region()->set_fade_out_active (true);
2459 tmp->hide_fade_line();
2461 XMLNode &after = alist->get_state();
2462 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2465 _editor->commit_reversible_command ();
2469 FadeOutDrag::aborted (bool)
2471 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2472 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2478 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2479 tmp->hide_fade_line();
2483 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2486 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2488 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2491 _points.push_back (Gnome::Art::Point (0, 0));
2492 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2495 MarkerDrag::~MarkerDrag ()
2497 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2503 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2505 Drag::start_grab (event, cursor);
2509 Location *location = _editor->find_location_from_marker (_marker, is_start);
2510 _editor->_dragging_edit_point = true;
2512 update_item (location);
2514 // _drag_line->show();
2515 // _line->raise_to_top();
2518 show_verbose_cursor_time (location->start());
2520 show_verbose_cursor_time (location->end());
2523 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2526 case Selection::Toggle:
2527 _editor->selection->toggle (_marker);
2529 case Selection::Set:
2530 if (!_editor->selection->selected (_marker)) {
2531 _editor->selection->set (_marker);
2534 case Selection::Extend:
2536 Locations::LocationList ll;
2537 list<Marker*> to_add;
2539 _editor->selection->markers.range (s, e);
2540 s = min (_marker->position(), s);
2541 e = max (_marker->position(), e);
2544 if (e < max_framepos) {
2547 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2548 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2549 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2552 to_add.push_back (lm->start);
2555 to_add.push_back (lm->end);
2559 if (!to_add.empty()) {
2560 _editor->selection->add (to_add);
2564 case Selection::Add:
2565 _editor->selection->add (_marker);
2569 /* Set up copies for us to manipulate during the drag */
2571 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2572 Location* l = _editor->find_location_from_marker (*i, is_start);
2573 _copied_locations.push_back (new Location (*l));
2578 MarkerDrag::setup_pointer_frame_offset ()
2581 Location *location = _editor->find_location_from_marker (_marker, is_start);
2582 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2586 MarkerDrag::motion (GdkEvent* event, bool)
2588 framecnt_t f_delta = 0;
2590 bool move_both = false;
2592 Location *real_location;
2593 Location *copy_location = 0;
2595 framepos_t const newframe = adjusted_current_frame (event);
2597 framepos_t next = newframe;
2599 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2603 MarkerSelection::iterator i;
2604 list<Location*>::iterator x;
2606 /* find the marker we're dragging, and compute the delta */
2608 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2609 x != _copied_locations.end() && i != _editor->selection->markers.end();
2615 if (marker == _marker) {
2617 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2622 if (real_location->is_mark()) {
2623 f_delta = newframe - copy_location->start();
2627 switch (marker->type()) {
2628 case Marker::SessionStart:
2629 case Marker::RangeStart:
2630 case Marker::LoopStart:
2631 case Marker::PunchIn:
2632 f_delta = newframe - copy_location->start();
2635 case Marker::SessionEnd:
2636 case Marker::RangeEnd:
2637 case Marker::LoopEnd:
2638 case Marker::PunchOut:
2639 f_delta = newframe - copy_location->end();
2642 /* what kind of marker is this ? */
2650 if (i == _editor->selection->markers.end()) {
2651 /* hmm, impossible - we didn't find the dragged marker */
2655 /* now move them all */
2657 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2658 x != _copied_locations.end() && i != _editor->selection->markers.end();
2664 /* call this to find out if its the start or end */
2666 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2670 if (real_location->locked()) {
2674 if (copy_location->is_mark()) {
2678 copy_location->set_start (copy_location->start() + f_delta);
2682 framepos_t new_start = copy_location->start() + f_delta;
2683 framepos_t new_end = copy_location->end() + f_delta;
2685 if (is_start) { // start-of-range marker
2688 copy_location->set_start (new_start);
2689 copy_location->set_end (new_end);
2690 } else if (new_start < copy_location->end()) {
2691 copy_location->set_start (new_start);
2692 } else if (newframe > 0) {
2693 _editor->snap_to (next, 1, true);
2694 copy_location->set_end (next);
2695 copy_location->set_start (newframe);
2698 } else { // end marker
2701 copy_location->set_end (new_end);
2702 copy_location->set_start (new_start);
2703 } else if (new_end > copy_location->start()) {
2704 copy_location->set_end (new_end);
2705 } else if (newframe > 0) {
2706 _editor->snap_to (next, -1, true);
2707 copy_location->set_start (next);
2708 copy_location->set_end (newframe);
2713 update_item (copy_location);
2715 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2718 lm->set_position (copy_location->start(), copy_location->end());
2722 assert (!_copied_locations.empty());
2724 show_verbose_cursor_time (newframe);
2727 _editor->update_canvas_now ();
2732 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2734 if (!movement_occurred) {
2736 /* just a click, do nothing but finish
2737 off the selection process
2740 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2743 case Selection::Set:
2744 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2745 _editor->selection->set (_marker);
2749 case Selection::Toggle:
2750 case Selection::Extend:
2751 case Selection::Add:
2758 _editor->_dragging_edit_point = false;
2760 _editor->begin_reversible_command ( _("move marker") );
2761 XMLNode &before = _editor->session()->locations()->get_state();
2763 MarkerSelection::iterator i;
2764 list<Location*>::iterator x;
2767 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2768 x != _copied_locations.end() && i != _editor->selection->markers.end();
2771 Location * location = _editor->find_location_from_marker (*i, is_start);
2775 if (location->locked()) {
2779 if (location->is_mark()) {
2780 location->set_start ((*x)->start());
2782 location->set ((*x)->start(), (*x)->end());
2787 XMLNode &after = _editor->session()->locations()->get_state();
2788 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2789 _editor->commit_reversible_command ();
2793 MarkerDrag::aborted (bool)
2799 MarkerDrag::update_item (Location*)
2804 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2806 _cumulative_x_drag (0),
2807 _cumulative_y_drag (0)
2809 if (_zero_gain_fraction < 0.0) {
2810 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2813 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2815 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2821 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2823 Drag::start_grab (event, _editor->cursors()->fader);
2825 // start the grab at the center of the control point so
2826 // the point doesn't 'jump' to the mouse after the first drag
2827 _fixed_grab_x = _point->get_x();
2828 _fixed_grab_y = _point->get_y();
2830 float const fraction = 1 - (_point->get_y() / _point->line().height());
2832 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2834 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2835 event->button.x + 10, event->button.y + 10);
2837 _editor->verbose_cursor()->show ();
2841 ControlPointDrag::motion (GdkEvent* event, bool)
2843 double dx = _drags->current_pointer_x() - last_pointer_x();
2844 double dy = _drags->current_pointer_y() - last_pointer_y();
2846 if (event->button.state & Keyboard::SecondaryModifier) {
2851 /* coordinate in pixels relative to the start of the region (for region-based automation)
2852 or track (for track-based automation) */
2853 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2854 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2856 // calculate zero crossing point. back off by .01 to stay on the
2857 // positive side of zero
2858 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2860 // make sure we hit zero when passing through
2861 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2865 if (_x_constrained) {
2868 if (_y_constrained) {
2872 _cumulative_x_drag = cx - _fixed_grab_x;
2873 _cumulative_y_drag = cy - _fixed_grab_y;
2877 cy = min ((double) _point->line().height(), cy);
2879 framepos_t cx_frames = _editor->unit_to_frame (cx);
2881 if (!_x_constrained) {
2882 _editor->snap_to_with_modifier (cx_frames, event);
2885 cx_frames = min (cx_frames, _point->line().maximum_time());
2887 float const fraction = 1.0 - (cy / _point->line().height());
2889 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2891 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2893 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2897 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2899 if (!movement_occurred) {
2903 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2904 _editor->reset_point_selection ();
2908 motion (event, false);
2911 _point->line().end_drag ();
2912 _editor->session()->commit_reversible_command ();
2916 ControlPointDrag::aborted (bool)
2918 _point->line().reset ();
2922 ControlPointDrag::active (Editing::MouseMode m)
2924 if (m == Editing::MouseGain) {
2925 /* always active in mouse gain */
2929 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2930 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2933 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2936 _cumulative_y_drag (0)
2938 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2942 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2944 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2947 _item = &_line->grab_item ();
2949 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2950 origin, and ditto for y.
2953 double cx = event->button.x;
2954 double cy = event->button.y;
2956 _line->parent_group().w2i (cx, cy);
2958 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2963 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2964 /* no adjacent points */
2968 Drag::start_grab (event, _editor->cursors()->fader);
2970 /* store grab start in parent frame */
2975 double fraction = 1.0 - (cy / _line->height());
2977 _line->start_drag_line (before, after, fraction);
2979 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2980 event->button.x + 10, event->button.y + 10);
2982 _editor->verbose_cursor()->show ();
2986 LineDrag::motion (GdkEvent* event, bool)
2988 double dy = _drags->current_pointer_y() - last_pointer_y();
2990 if (event->button.state & Keyboard::SecondaryModifier) {
2994 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2996 _cumulative_y_drag = cy - _fixed_grab_y;
2999 cy = min ((double) _line->height(), cy);
3001 double const fraction = 1.0 - (cy / _line->height());
3005 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3011 /* we are ignoring x position for this drag, so we can just pass in anything */
3012 _line->drag_motion (0, fraction, true, push);
3014 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3018 LineDrag::finished (GdkEvent* event, bool)
3020 motion (event, false);
3022 _editor->session()->commit_reversible_command ();
3026 LineDrag::aborted (bool)
3031 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3034 _cumulative_x_drag (0)
3036 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3040 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3042 Drag::start_grab (event);
3044 _line = reinterpret_cast<Line*> (_item);
3047 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3049 double cx = event->button.x;
3050 double cy = event->button.y;
3052 _item->property_parent().get_value()->w2i(cx, cy);
3054 /* store grab start in parent frame */
3055 _region_view_grab_x = cx;
3057 _before = *(float*) _item->get_data ("position");
3059 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3061 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3065 FeatureLineDrag::motion (GdkEvent*, bool)
3067 double dx = _drags->current_pointer_x() - last_pointer_x();
3069 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3071 _cumulative_x_drag += dx;
3073 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3082 ArdourCanvas::Points points;
3084 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3086 _line->get_bounds(x1, y2, x2, y2);
3088 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3089 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3091 _line->property_points() = points;
3093 float *pos = new float;
3096 _line->set_data ("position", pos);
3102 FeatureLineDrag::finished (GdkEvent*, bool)
3104 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3105 _arv->update_transient(_before, _before);
3109 FeatureLineDrag::aborted (bool)
3114 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3116 , _vertical_only (false)
3118 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3122 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3124 Drag::start_grab (event);
3125 show_verbose_cursor_time (adjusted_current_frame (event));
3129 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3136 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3138 framepos_t grab = grab_frame ();
3139 if (Config->get_rubberbanding_snaps_to_grid ()) {
3140 _editor->snap_to_with_modifier (grab, event);
3143 /* base start and end on initial click position */
3153 if (_drags->current_pointer_y() < grab_y()) {
3154 y1 = _drags->current_pointer_y();
3157 y2 = _drags->current_pointer_y();
3162 if (start != end || y1 != y2) {
3164 double x1 = _editor->frame_to_pixel (start);
3165 double x2 = _editor->frame_to_pixel (end);
3167 _editor->rubberband_rect->property_x1() = x1;
3168 if (_vertical_only) {
3169 /* fixed 10 pixel width */
3170 _editor->rubberband_rect->property_x2() = x1 + 10;
3172 _editor->rubberband_rect->property_x2() = x2;
3175 _editor->rubberband_rect->property_y1() = y1;
3176 _editor->rubberband_rect->property_y2() = y2;
3178 _editor->rubberband_rect->show();
3179 _editor->rubberband_rect->raise_to_top();
3181 show_verbose_cursor_time (pf);
3183 do_select_things (event, true);
3188 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3193 if (grab_frame() < last_pointer_frame()) {
3195 x2 = last_pointer_frame ();
3198 x1 = last_pointer_frame ();
3204 if (_drags->current_pointer_y() < grab_y()) {
3205 y1 = _drags->current_pointer_y();
3208 y2 = _drags->current_pointer_y();
3212 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3216 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3218 if (movement_occurred) {
3220 motion (event, false);
3221 do_select_things (event, false);
3227 bool do_deselect = true;
3228 MidiTimeAxisView* mtv;
3230 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3232 if (_editor->selection->empty()) {
3233 /* nothing selected */
3234 add_midi_region (mtv);
3235 do_deselect = false;
3245 _editor->rubberband_rect->hide();
3249 RubberbandSelectDrag::aborted (bool)
3251 _editor->rubberband_rect->hide ();
3254 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3255 : RegionDrag (e, i, p, v)
3257 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3261 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3263 Drag::start_grab (event, cursor);
3265 show_verbose_cursor_time (adjusted_current_frame (event));
3269 TimeFXDrag::motion (GdkEvent* event, bool)
3271 RegionView* rv = _primary;
3273 framepos_t const pf = adjusted_current_frame (event);
3275 if (pf > rv->region()->position()) {
3276 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3279 show_verbose_cursor_time (pf);
3283 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3285 _primary->get_time_axis_view().hide_timestretch ();
3287 if (!movement_occurred) {
3291 if (last_pointer_frame() < _primary->region()->position()) {
3292 /* backwards drag of the left edge - not usable */
3296 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3298 float percentage = (double) newlen / (double) _primary->region()->length();
3300 #ifndef USE_RUBBERBAND
3301 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3302 if (_primary->region()->data_type() == DataType::AUDIO) {
3303 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3307 if (!_editor->get_selection().regions.empty()) {
3308 /* primary will already be included in the selection, and edit
3309 group shared editing will propagate selection across
3310 equivalent regions, so just use the current region
3314 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3315 error << _("An error occurred while executing time stretch operation") << endmsg;
3321 TimeFXDrag::aborted (bool)
3323 _primary->get_time_axis_view().hide_timestretch ();
3326 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3329 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3333 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3335 Drag::start_grab (event);
3339 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3341 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3345 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3347 if (movement_occurred && _editor->session()) {
3348 /* make sure we stop */
3349 _editor->session()->request_transport_speed (0.0);
3354 ScrubDrag::aborted (bool)
3359 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3363 , _original_pointer_time_axis (-1)
3364 , _last_pointer_time_axis (-1)
3366 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3370 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3372 if (_editor->session() == 0) {
3376 Gdk::Cursor* cursor = 0;
3378 switch (_operation) {
3379 case CreateSelection:
3380 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3385 cursor = _editor->cursors()->selector;
3386 Drag::start_grab (event, cursor);
3389 case SelectionStartTrim:
3390 if (_editor->clicked_axisview) {
3391 _editor->clicked_axisview->order_selection_trims (_item, true);
3393 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3396 case SelectionEndTrim:
3397 if (_editor->clicked_axisview) {
3398 _editor->clicked_axisview->order_selection_trims (_item, false);
3400 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3404 Drag::start_grab (event, cursor);
3408 if (_operation == SelectionMove) {
3409 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3411 show_verbose_cursor_time (adjusted_current_frame (event));
3414 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3418 SelectionDrag::setup_pointer_frame_offset ()
3420 switch (_operation) {
3421 case CreateSelection:
3422 _pointer_frame_offset = 0;
3425 case SelectionStartTrim:
3427 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3430 case SelectionEndTrim:
3431 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3437 SelectionDrag::motion (GdkEvent* event, bool first_move)
3439 framepos_t start = 0;
3443 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3444 if (pending_time_axis.first == 0) {
3448 framepos_t const pending_position = adjusted_current_frame (event);
3450 /* only alter selection if things have changed */
3452 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3456 switch (_operation) {
3457 case CreateSelection:
3459 framepos_t grab = grab_frame ();
3462 _editor->snap_to (grab);
3465 if (pending_position < grab_frame()) {
3466 start = pending_position;
3469 end = pending_position;
3473 /* first drag: Either add to the selection
3474 or create a new selection
3480 /* adding to the selection */
3481 _editor->set_selected_track_as_side_effect (Selection::Add);
3482 //_editor->selection->add (_editor->clicked_axisview);
3483 _editor->clicked_selection = _editor->selection->add (start, end);
3488 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3489 //_editor->selection->set (_editor->clicked_axisview);
3490 _editor->set_selected_track_as_side_effect (Selection::Set);
3493 _editor->clicked_selection = _editor->selection->set (start, end);
3497 /* select the track that we're in */
3498 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3499 // _editor->set_selected_track_as_side_effect (Selection::Add);
3500 _editor->selection->add (pending_time_axis.first);
3501 _added_time_axes.push_back (pending_time_axis.first);
3504 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3505 tracks that we selected in the first place.
3508 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3509 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3511 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3512 while (i != _added_time_axes.end()) {
3514 list<TimeAxisView*>::iterator tmp = i;
3517 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3518 _editor->selection->remove (*i);
3519 _added_time_axes.remove (*i);
3528 case SelectionStartTrim:
3530 start = _editor->selection->time[_editor->clicked_selection].start;
3531 end = _editor->selection->time[_editor->clicked_selection].end;
3533 if (pending_position > end) {
3536 start = pending_position;
3540 case SelectionEndTrim:
3542 start = _editor->selection->time[_editor->clicked_selection].start;
3543 end = _editor->selection->time[_editor->clicked_selection].end;
3545 if (pending_position < start) {
3548 end = pending_position;
3555 start = _editor->selection->time[_editor->clicked_selection].start;
3556 end = _editor->selection->time[_editor->clicked_selection].end;
3558 length = end - start;
3560 start = pending_position;
3561 _editor->snap_to (start);
3563 end = start + length;
3568 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3569 _editor->start_canvas_autoscroll (1, 0);
3573 _editor->selection->replace (_editor->clicked_selection, start, end);
3576 if (_operation == SelectionMove) {
3577 show_verbose_cursor_time(start);
3579 show_verbose_cursor_time(pending_position);
3584 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3586 Session* s = _editor->session();
3588 if (movement_occurred) {
3589 motion (event, false);
3590 /* XXX this is not object-oriented programming at all. ick */
3591 if (_editor->selection->time.consolidate()) {
3592 _editor->selection->TimeChanged ();
3595 /* XXX what if its a music time selection? */
3596 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3597 s->request_play_range (&_editor->selection->time, true);
3602 /* just a click, no pointer movement.*/
3604 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3605 _editor->selection->clear_time();
3608 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3609 _editor->selection->set (_editor->clicked_axisview);
3612 if (s && s->get_play_range () && s->transport_rolling()) {
3613 s->request_stop (false, false);
3618 _editor->stop_canvas_autoscroll ();
3622 SelectionDrag::aborted (bool)
3627 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3632 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3634 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3635 physical_screen_height (_editor->get_window()));
3636 _drag_rect->hide ();
3638 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3639 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3643 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3645 if (_editor->session() == 0) {
3649 Gdk::Cursor* cursor = 0;
3651 if (!_editor->temp_location) {
3652 _editor->temp_location = new Location (*_editor->session());
3655 switch (_operation) {
3656 case CreateRangeMarker:
3657 case CreateTransportMarker:
3658 case CreateCDMarker:
3660 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3665 cursor = _editor->cursors()->selector;
3669 Drag::start_grab (event, cursor);
3671 show_verbose_cursor_time (adjusted_current_frame (event));
3675 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3677 framepos_t start = 0;
3679 ArdourCanvas::SimpleRect *crect;
3681 switch (_operation) {
3682 case CreateRangeMarker:
3683 crect = _editor->range_bar_drag_rect;
3685 case CreateTransportMarker:
3686 crect = _editor->transport_bar_drag_rect;
3688 case CreateCDMarker:
3689 crect = _editor->cd_marker_bar_drag_rect;
3692 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3697 framepos_t const pf = adjusted_current_frame (event);
3699 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3700 framepos_t grab = grab_frame ();
3701 _editor->snap_to (grab);
3703 if (pf < grab_frame()) {
3711 /* first drag: Either add to the selection
3712 or create a new selection.
3717 _editor->temp_location->set (start, end);
3721 update_item (_editor->temp_location);
3723 //_drag_rect->raise_to_top();
3728 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3729 _editor->start_canvas_autoscroll (1, 0);
3733 _editor->temp_location->set (start, end);
3735 double x1 = _editor->frame_to_pixel (start);
3736 double x2 = _editor->frame_to_pixel (end);
3737 crect->property_x1() = x1;
3738 crect->property_x2() = x2;
3740 update_item (_editor->temp_location);
3743 show_verbose_cursor_time (pf);
3748 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3750 Location * newloc = 0;
3754 if (movement_occurred) {
3755 motion (event, false);
3758 switch (_operation) {
3759 case CreateRangeMarker:
3760 case CreateCDMarker:
3762 _editor->begin_reversible_command (_("new range marker"));
3763 XMLNode &before = _editor->session()->locations()->get_state();
3764 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3765 if (_operation == CreateCDMarker) {
3766 flags = Location::IsRangeMarker | Location::IsCDMarker;
3767 _editor->cd_marker_bar_drag_rect->hide();
3770 flags = Location::IsRangeMarker;
3771 _editor->range_bar_drag_rect->hide();
3773 newloc = new Location (
3774 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3777 _editor->session()->locations()->add (newloc, true);
3778 XMLNode &after = _editor->session()->locations()->get_state();
3779 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3780 _editor->commit_reversible_command ();
3784 case CreateTransportMarker:
3785 // popup menu to pick loop or punch
3786 _editor->new_transport_marker_context_menu (&event->button, _item);
3790 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3792 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3797 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3799 if (end == max_framepos) {
3800 end = _editor->session()->current_end_frame ();
3803 if (start == max_framepos) {
3804 start = _editor->session()->current_start_frame ();
3807 switch (_editor->mouse_mode) {
3809 /* find the two markers on either side and then make the selection from it */
3810 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3814 /* find the two markers on either side of the click and make the range out of it */
3815 _editor->selection->set (start, end);
3824 _editor->stop_canvas_autoscroll ();
3828 RangeMarkerBarDrag::aborted (bool)
3834 RangeMarkerBarDrag::update_item (Location* location)
3836 double const x1 = _editor->frame_to_pixel (location->start());
3837 double const x2 = _editor->frame_to_pixel (location->end());
3839 _drag_rect->property_x1() = x1;
3840 _drag_rect->property_x2() = x2;
3843 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3847 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3851 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3853 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3854 Drag::start_grab (event, _editor->cursors()->zoom_out);
3857 Drag::start_grab (event, _editor->cursors()->zoom_in);
3861 show_verbose_cursor_time (adjusted_current_frame (event));
3865 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3870 framepos_t const pf = adjusted_current_frame (event);
3872 framepos_t grab = grab_frame ();
3873 _editor->snap_to_with_modifier (grab, event);
3875 /* base start and end on initial click position */
3887 _editor->zoom_rect->show();
3888 _editor->zoom_rect->raise_to_top();
3891 _editor->reposition_zoom_rect(start, end);
3893 show_verbose_cursor_time (pf);
3898 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3900 if (movement_occurred) {
3901 motion (event, false);
3903 if (grab_frame() < last_pointer_frame()) {
3904 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3906 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3909 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3910 _editor->tav_zoom_step (_zoom_out);
3912 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3916 _editor->zoom_rect->hide();
3920 MouseZoomDrag::aborted (bool)
3922 _editor->zoom_rect->hide ();
3925 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3927 , _cumulative_dx (0)
3928 , _cumulative_dy (0)
3930 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3932 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3933 _region = &_primary->region_view ();
3934 _note_height = _region->midi_stream_view()->note_height ();
3938 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3940 Drag::start_grab (event);
3942 if (!(_was_selected = _primary->selected())) {
3944 /* tertiary-click means extend selection - we'll do that on button release,
3945 so don't add it here, because otherwise we make it hard to figure
3946 out the "extend-to" range.
3949 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3952 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3955 _region->note_selected (_primary, true);
3957 _region->unique_select (_primary);
3963 /** @return Current total drag x change in frames */
3965 NoteDrag::total_dx () const
3968 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3970 /* primary note time */
3971 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3973 /* new time of the primary note in session frames */
3974 frameoffset_t st = n + dx;
3976 framepos_t const rp = _region->region()->position ();
3978 /* prevent the note being dragged earlier than the region's position */
3981 /* snap and return corresponding delta */
3982 return _region->snap_frame_to_frame (st - rp) + rp - n;
3985 /** @return Current total drag y change in note number */
3987 NoteDrag::total_dy () const
3989 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3993 NoteDrag::motion (GdkEvent *, bool)
3995 /* Total change in x and y since the start of the drag */
3996 frameoffset_t const dx = total_dx ();
3997 int8_t const dy = total_dy ();
3999 /* Now work out what we have to do to the note canvas items to set this new drag delta */
4000 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
4001 double const tdy = -dy * _note_height - _cumulative_dy;
4004 _cumulative_dx += tdx;
4005 _cumulative_dy += tdy;
4007 int8_t note_delta = total_dy();
4009 _region->move_selection (tdx, tdy, note_delta);
4011 /* the new note value may be the same as the old one, but we
4012 * don't know what that means because the selection may have
4013 * involved more than one note and we might be doing something
4014 * odd with them. so show the note value anyway, always.
4018 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4020 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4021 (int) floor (new_note));
4023 show_verbose_cursor_text (buf);
4028 NoteDrag::finished (GdkEvent* ev, bool moved)
4031 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4033 if (_was_selected) {
4034 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4036 _region->note_deselected (_primary);
4039 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4040 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4042 if (!extend && !add && _region->selection_size() > 1) {
4043 _region->unique_select (_primary);
4044 } else if (extend) {
4045 _region->note_selected (_primary, true, true);
4047 /* it was added during button press */
4052 _region->note_dropped (_primary, total_dx(), total_dy());
4057 NoteDrag::aborted (bool)
4062 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4063 : Drag (editor, item)
4065 , _nothing_to_drag (false)
4067 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4069 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4072 /* get all lines in the automation view */
4073 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4075 /* find those that overlap the ranges being dragged */
4076 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4077 while (i != lines.end ()) {
4078 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4081 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4083 /* check this range against all the AudioRanges that we are using */
4084 list<AudioRange>::const_iterator k = _ranges.begin ();
4085 while (k != _ranges.end()) {
4086 if (k->coverage (r.first, r.second) != OverlapNone) {
4092 /* add it to our list if it overlaps at all */
4093 if (k != _ranges.end()) {
4098 _lines.push_back (n);
4104 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4108 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4110 Drag::start_grab (event, cursor);
4112 /* Get line states before we start changing things */
4113 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4114 i->state = &i->line->get_state ();
4117 if (_ranges.empty()) {
4119 /* No selected time ranges: drag all points */
4120 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4121 uint32_t const N = i->line->npoints ();
4122 for (uint32_t j = 0; j < N; ++j) {
4123 i->points.push_back (i->line->nth (j));
4129 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4131 framecnt_t const half = (i->start + i->end) / 2;
4133 /* find the line that this audio range starts in */
4134 list<Line>::iterator j = _lines.begin();
4135 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4139 if (j != _lines.end()) {
4140 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4142 /* j is the line that this audio range starts in; fade into it;
4143 64 samples length plucked out of thin air.
4146 framepos_t a = i->start + 64;
4151 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4152 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4154 the_list->add (p, the_list->eval (p));
4155 j->line->add_always_in_view (p);
4156 the_list->add (q, the_list->eval (q));
4157 j->line->add_always_in_view (q);
4160 /* same thing for the end */
4163 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4167 if (j != _lines.end()) {
4168 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4170 /* j is the line that this audio range starts in; fade out of it;
4171 64 samples length plucked out of thin air.
4174 framepos_t b = i->end - 64;
4179 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4180 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4182 the_list->add (p, the_list->eval (p));
4183 j->line->add_always_in_view (p);
4184 the_list->add (q, the_list->eval (q));
4185 j->line->add_always_in_view (q);
4189 _nothing_to_drag = true;
4191 /* Find all the points that should be dragged and put them in the relevant
4192 points lists in the Line structs.
4195 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4197 uint32_t const N = i->line->npoints ();
4198 for (uint32_t j = 0; j < N; ++j) {
4200 /* here's a control point on this line */
4201 ControlPoint* p = i->line->nth (j);
4202 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4204 /* see if it's inside a range */
4205 list<AudioRange>::const_iterator k = _ranges.begin ();
4206 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4210 if (k != _ranges.end()) {
4211 /* dragging this point */
4212 _nothing_to_drag = false;
4213 i->points.push_back (p);
4219 if (_nothing_to_drag) {
4223 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4224 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4229 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4231 if (_nothing_to_drag) {
4235 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4236 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4238 /* we are ignoring x position for this drag, so we can just pass in anything */
4239 i->line->drag_motion (0, f, true, false);
4244 AutomationRangeDrag::finished (GdkEvent* event, bool)
4246 if (_nothing_to_drag) {
4250 motion (event, false);
4251 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4252 i->line->end_drag ();
4253 i->line->clear_always_in_view ();
4256 _editor->session()->commit_reversible_command ();
4260 AutomationRangeDrag::aborted (bool)
4262 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4263 i->line->clear_always_in_view ();
4268 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4271 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4272 layer = v->region()->layer ();
4273 initial_y = v->get_canvas_group()->property_y ();
4274 initial_playlist = v->region()->playlist ();
4275 initial_position = v->region()->position ();
4276 initial_end = v->region()->position () + v->region()->length ();
4279 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4283 , _cumulative_dx (0)
4285 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4289 PatchChangeDrag::motion (GdkEvent* ev, bool)
4291 framepos_t f = adjusted_current_frame (ev);
4292 boost::shared_ptr<Region> r = _region_view->region ();
4293 f = max (f, r->position ());
4294 f = min (f, r->last_frame ());
4296 framecnt_t const dxf = f - grab_frame();
4297 double const dxu = _editor->frame_to_unit (dxf);
4298 _patch_change->move (dxu - _cumulative_dx, 0);
4299 _cumulative_dx = dxu;
4303 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4305 if (!movement_occurred) {
4309 boost::shared_ptr<Region> r (_region_view->region ());
4311 framepos_t f = adjusted_current_frame (ev);
4312 f = max (f, r->position ());
4313 f = min (f, r->last_frame ());
4315 _region_view->move_patch_change (
4317 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4322 PatchChangeDrag::aborted (bool)
4324 _patch_change->move (-_cumulative_dx, 0);
4328 PatchChangeDrag::setup_pointer_frame_offset ()
4330 boost::shared_ptr<Region> region = _region_view->region ();
4331 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4334 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4335 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4342 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4344 framepos_t const p = _region_view->region()->position ();
4345 double const y = _region_view->midi_view()->y_position ();
4347 x1 = max ((framepos_t) 0, x1 - p);
4348 x2 = max ((framepos_t) 0, x2 - p);
4349 y1 = max (0.0, y1 - y);
4350 y2 = max (0.0, y2 - y);
4352 _region_view->update_drag_selection (
4353 _editor->frame_to_pixel (x1),
4354 _editor->frame_to_pixel (x2),
4357 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4362 MidiRubberbandSelectDrag::deselect_things ()
4367 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4368 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4371 _vertical_only = true;
4375 MidiVerticalSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4377 double const y = _region_view->midi_view()->y_position ();
4379 y1 = max (0.0, y1 - y);
4380 y2 = max (0.0, y2 - y);
4382 _region_view->update_vertical_drag_selection (
4385 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4390 MidiVerticalSelectDrag::deselect_things ()
4395 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4396 : RubberbandSelectDrag (e, i)
4402 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4404 if (drag_in_progress) {
4405 /* We just want to select things at the end of the drag, not during it */
4409 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4411 _editor->begin_reversible_command (_("rubberband selection"));
4412 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4413 _editor->commit_reversible_command ();
4417 EditorRubberbandSelectDrag::deselect_things ()
4419 if (!getenv("ARDOUR_SAE")) {
4420 _editor->selection->clear_tracks();
4422 _editor->selection->clear_regions();
4423 _editor->selection->clear_points ();
4424 _editor->selection->clear_lines ();
4427 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4435 NoteCreateDrag::~NoteCreateDrag ()
4441 NoteCreateDrag::grid_frames (framepos_t t) const
4444 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4449 return _region_view->region_beats_to_region_frames (grid_beats);
4453 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4455 Drag::start_grab (event, cursor);
4457 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4459 framepos_t pf = _drags->current_pointer_frame ();
4460 framecnt_t const g = grid_frames (pf);
4462 /* Hack so that we always snap to the note that we are over, instead of snapping
4463 to the next one if we're more than halfway through the one we're over.
4465 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4469 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4471 MidiStreamView* sv = _region_view->midi_stream_view ();
4472 double const x = _editor->frame_to_pixel (_note[0]);
4473 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4475 _drag_rect->property_x1() = x;
4476 _drag_rect->property_y1() = y;
4477 _drag_rect->property_x2() = x;
4478 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4480 _drag_rect->property_outline_what() = 0xff;
4481 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4482 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4486 NoteCreateDrag::motion (GdkEvent* event, bool)
4488 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4489 double const x = _editor->frame_to_pixel (_note[1]);
4490 if (_note[1] > _note[0]) {
4491 _drag_rect->property_x2() = x;
4493 _drag_rect->property_x1() = x;
4498 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4500 if (!had_movement) {
4504 framepos_t const start = min (_note[0], _note[1]);
4505 framecnt_t length = abs (_note[0] - _note[1]);
4507 framecnt_t const g = grid_frames (start);
4508 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4510 if (_editor->snap_mode() == SnapNormal && length < g) {
4511 length = g - one_tick;
4514 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4516 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4520 NoteCreateDrag::y_to_region (double y) const
4523 _region_view->get_canvas_group()->w2i (x, y);
4528 NoteCreateDrag::aborted (bool)