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
400 struct EditorOrderTimeAxisViewSorter {
401 bool operator() (TimeAxisView* a, TimeAxisView* b) {
402 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
403 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
405 return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
409 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
413 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
415 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
416 as some of the regions we are dragging may be on such tracks.
419 TrackViewList track_views = _editor->track_views;
420 track_views.sort (EditorOrderTimeAxisViewSorter ());
422 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
423 _time_axis_views.push_back (*i);
425 TimeAxisView::Children children_list = (*i)->get_child_list ();
426 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
427 _time_axis_views.push_back (j->get());
431 /* the list of views can be empty at this point if this is a region list-insert drag
434 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
435 _views.push_back (DraggingView (*i, this));
438 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
442 RegionDrag::region_going_away (RegionView* v)
444 list<DraggingView>::iterator i = _views.begin ();
445 while (i != _views.end() && i->view != v) {
449 if (i != _views.end()) {
454 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
455 * or -1 if it is not found.
458 RegionDrag::find_time_axis_view (TimeAxisView* t) const
461 int const N = _time_axis_views.size ();
462 while (i < N && _time_axis_views[i] != t) {
473 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
474 : RegionDrag (e, i, p, v),
483 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
485 Drag::start_grab (event, cursor);
487 show_verbose_cursor_time (_last_frame_position);
489 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
490 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
491 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
495 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
497 /* compute the amount of pointer motion in frames, and where
498 the region would be if we moved it by that much.
500 *pending_region_position = adjusted_current_frame (event);
502 framepos_t sync_frame;
503 framecnt_t sync_offset;
506 sync_offset = _primary->region()->sync_offset (sync_dir);
508 /* we don't handle a sync point that lies before zero.
510 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
512 sync_frame = *pending_region_position + (sync_dir*sync_offset);
514 _editor->snap_to_with_modifier (sync_frame, event);
516 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
519 *pending_region_position = _last_frame_position;
522 if (*pending_region_position > max_framepos - _primary->region()->length()) {
523 *pending_region_position = _last_frame_position;
528 /* in locked edit mode, reverse the usual meaning of _x_constrained */
529 bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
531 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
533 /* x movement since last time (in pixels) */
534 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
536 /* total x movement */
537 framecnt_t total_dx = *pending_region_position;
538 if (regions_came_from_canvas()) {
539 total_dx = total_dx - grab_frame ();
542 /* check that no regions have gone off the start of the session */
543 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
544 if ((i->view->region()->position() + total_dx) < 0) {
546 *pending_region_position = _last_frame_position;
551 _last_frame_position = *pending_region_position;
558 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
560 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
561 int const n = i->time_axis_view + delta_track;
562 if (n < 0 || n >= int (_time_axis_views.size())) {
563 /* off the top or bottom track */
567 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
568 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
569 /* not a track, or the wrong type */
573 double const l = i->layer + delta_layer;
575 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
576 mode to allow the user to place a region below another on layer 0.
578 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
579 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
580 If it has, the layers will be munged later anyway, so it's ok.
586 /* all regions being dragged are ok with this change */
591 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
593 assert (!_views.empty ());
595 /* Find the TimeAxisView that the pointer is now over */
596 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
598 if (first_move && tv.first->view()->layer_display() == Stacked) {
599 tv.first->view()->set_layer_display (Expanded);
602 /* Bail early if we're not over a track */
603 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
604 if (!rtv || !rtv->is_track()) {
605 _editor->verbose_cursor()->hide ();
609 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
611 /* Here's the current pointer position in terms of time axis view and layer */
612 int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
613 double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
615 /* Work out the change in x */
616 framepos_t pending_region_position;
617 double const x_delta = compute_x_delta (event, &pending_region_position);
619 /* Work out the change in y */
620 int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
621 double delta_layer = current_pointer_layer - _last_pointer_layer;
623 if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
624 /* this y movement is not allowed, so do no y movement this time */
625 delta_time_axis_view = 0;
629 if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
630 /* haven't reached next snap point, and we're not switching
631 trackviews nor layers. nothing to do.
636 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
638 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
640 RegionView* rv = i->view;
642 if (rv->region()->locked()) {
648 rv->get_time_axis_view().hide_dependent_views (*rv);
650 /* Absolutely no idea why this is necessary, but it is; without
651 it, the region view disappears after the reparent.
653 _editor->update_canvas_now ();
655 /* Reparent to a non scrolling group so that we can keep the
656 region selection above all time axis views.
657 Reparenting means that we will have to move the region view
658 later, as the two parent groups have different coordinates.
661 rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
663 rv->fake_set_opaque (true);
666 /* If we have moved tracks, we'll fudge the layer delta so that the
667 region gets moved back onto layer 0 on its new track; this avoids
668 confusion when dragging regions from non-zero layers onto different
671 double this_delta_layer = delta_layer;
672 if (delta_time_axis_view != 0) {
673 this_delta_layer = - i->layer;
676 /* The TimeAxisView that this region is now on */
677 TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
679 /* Ensure it is moved from stacked -> expanded if appropriate */
680 if (tv->view()->layer_display() == Stacked) {
681 tv->view()->set_layer_display (Expanded);
684 /* We're only allowed to go -ve in layer on Expanded views */
685 if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
686 this_delta_layer = - i->layer;
690 rv->set_height (tv->view()->child_height ());
692 /* Update show/hidden status as the region view may have come from a hidden track,
693 or have moved to one.
696 rv->get_canvas_group()->hide ();
698 rv->get_canvas_group()->show ();
701 /* Update the DraggingView */
702 i->time_axis_view += delta_time_axis_view;
703 i->layer += this_delta_layer;
706 _editor->mouse_brush_insert_region (rv, pending_region_position);
711 /* Get the y coordinate of the top of the track that this region is now on */
712 tv->canvas_display()->i2w (x, y);
713 y += _editor->get_trackview_group_vertical_offset();
715 /* And adjust for the layer that it should be on */
716 StreamView* cv = tv->view ();
717 switch (cv->layer_display ()) {
721 y += (cv->layers() - i->layer - 1) * cv->child_height ();
724 y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
728 /* Now move the region view */
729 rv->move (x_delta, y - rv->get_canvas_group()->property_y());
732 } /* foreach region */
734 _total_x_delta += x_delta;
737 _editor->cursor_group->raise_to_top();
740 if (x_delta != 0 && !_brushing) {
741 show_verbose_cursor_time (_last_frame_position);
744 _last_pointer_time_axis_view += delta_time_axis_view;
745 _last_pointer_layer += delta_layer;
749 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
751 if (_copy && first_move) {
753 /* duplicate the regionview(s) and region(s) */
755 list<DraggingView> new_regionviews;
757 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
759 RegionView* rv = i->view;
760 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
761 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
763 const boost::shared_ptr<const Region> original = rv->region();
764 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
765 region_copy->set_position (original->position());
769 boost::shared_ptr<AudioRegion> audioregion_copy
770 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
772 nrv = new AudioRegionView (*arv, audioregion_copy);
774 boost::shared_ptr<MidiRegion> midiregion_copy
775 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
776 nrv = new MidiRegionView (*mrv, midiregion_copy);
781 nrv->get_canvas_group()->show ();
782 new_regionviews.push_back (DraggingView (nrv, this));
784 /* swap _primary to the copy */
786 if (rv == _primary) {
790 /* ..and deselect the one we copied */
792 rv->set_selected (false);
795 if (!new_regionviews.empty()) {
797 /* reflect the fact that we are dragging the copies */
799 _views = new_regionviews;
801 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
804 sync the canvas to what we think is its current state
805 without it, the canvas seems to
806 "forget" to update properly after the upcoming reparent()
807 ..only if the mouse is in rapid motion at the time of the grab.
808 something to do with regionview creation taking so long?
810 _editor->update_canvas_now();
814 RegionMotionDrag::motion (event, first_move);
818 RegionMotionDrag::finished (GdkEvent *, bool)
820 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
825 if ((*i)->view()->layer_display() == Expanded) {
826 (*i)->view()->set_layer_display (Stacked);
832 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
834 RegionMotionDrag::finished (ev, movement_occurred);
836 if (!movement_occurred) {
841 /* reverse this here so that we have the correct logic to finalize
845 if (Config->get_edit_mode() == Lock) {
846 _x_constrained = !_x_constrained;
849 assert (!_views.empty ());
851 /* We might have hidden region views so that they weren't visible during the drag
852 (when they have been reparented). Now everything can be shown again, as region
853 views are back in their track parent groups.
855 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
856 i->view->get_canvas_group()->show ();
859 bool const changed_position = (_last_frame_position != _primary->region()->position());
860 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
861 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
863 _editor->update_canvas_now ();
885 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
887 RegionSelection new_views;
888 PlaylistSet modified_playlists;
889 list<RegionView*> views_to_delete;
892 /* all changes were made during motion event handlers */
894 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
898 _editor->commit_reversible_command ();
902 if (_x_constrained) {
903 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
905 _editor->begin_reversible_command (Operations::region_copy);
908 /* insert the regions into their new playlists */
909 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
911 if (i->view->region()->locked()) {
917 if (changed_position && !_x_constrained) {
918 where = i->view->region()->position() - drag_delta;
920 where = i->view->region()->position();
923 RegionView* new_view = insert_region_into_playlist (
924 i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
931 new_views.push_back (new_view);
933 /* we don't need the copied RegionView any more */
934 views_to_delete.push_back (i->view);
937 /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
938 because when views are deleted they are automagically removed from _views, which messes
941 for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
945 /* If we've created new regions either by copying or moving
946 to a new track, we want to replace the old selection with the new ones
949 if (new_views.size() > 0) {
950 _editor->selection->set (new_views);
953 /* write commands for the accumulated diffs for all our modified playlists */
954 add_stateful_diff_commands_for_playlists (modified_playlists);
956 _editor->commit_reversible_command ();
960 RegionMoveDrag::finished_no_copy (
961 bool const changed_position,
962 bool const changed_tracks,
963 framecnt_t const drag_delta
966 RegionSelection new_views;
967 PlaylistSet modified_playlists;
968 PlaylistSet frozen_playlists;
969 set<RouteTimeAxisView*> views_to_update;
972 /* all changes were made during motion event handlers */
973 _editor->commit_reversible_command ();
977 if (_x_constrained) {
978 _editor->begin_reversible_command (_("fixed time region drag"));
980 _editor->begin_reversible_command (Operations::region_drag);
983 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
985 RegionView* rv = i->view;
987 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
988 double const dest_layer = i->layer;
990 if (rv->region()->locked()) {
995 views_to_update.insert (dest_rtv);
999 if (changed_position && !_x_constrained) {
1000 where = rv->region()->position() - drag_delta;
1002 where = rv->region()->position();
1005 if (changed_tracks) {
1007 /* insert into new playlist */
1009 RegionView* new_view = insert_region_into_playlist (
1010 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1013 if (new_view == 0) {
1018 new_views.push_back (new_view);
1020 /* remove from old playlist */
1022 /* the region that used to be in the old playlist is not
1023 moved to the new one - we use a copy of it. as a result,
1024 any existing editor for the region should no longer be
1027 rv->hide_region_editor();
1028 rv->fake_set_opaque (false);
1030 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1034 rv->region()->clear_changes ();
1037 motion on the same track. plonk the previously reparented region
1038 back to its original canvas group (its streamview).
1039 No need to do anything for copies as they are fake regions which will be deleted.
1042 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1043 rv->get_canvas_group()->property_y() = i->initial_y;
1044 rv->get_time_axis_view().reveal_dependent_views (*rv);
1046 /* just change the model */
1048 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1050 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1051 playlist->set_layer (rv->region(), dest_layer);
1054 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1056 pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
1059 playlist->freeze ();
1062 /* this movement may result in a crossfade being modified, so we need to get undo
1063 data from the playlist as well as the region.
1066 r = modified_playlists.insert (playlist);
1068 playlist->clear_changes ();
1071 rv->region()->set_position (where);
1073 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1076 if (changed_tracks) {
1078 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1079 was selected in all of them, then removing it from a playlist will have removed all
1080 trace of it from _views (i.e. there were N regions selected, we removed 1,
1081 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1082 corresponding regionview, and _views is now empty).
1084 This could have invalidated any and all iterators into _views.
1086 The heuristic we use here is: if the region selection is empty, break out of the loop
1087 here. if the region selection is not empty, then restart the loop because we know that
1088 we must have removed at least the region(view) we've just been working on as well as any
1089 that we processed on previous iterations.
1091 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1092 we can just iterate.
1096 if (_views.empty()) {
1107 /* If we've created new regions either by copying or moving
1108 to a new track, we want to replace the old selection with the new ones
1111 if (new_views.size() > 0) {
1112 _editor->selection->set (new_views);
1115 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1119 /* write commands for the accumulated diffs for all our modified playlists */
1120 add_stateful_diff_commands_for_playlists (modified_playlists);
1122 _editor->commit_reversible_command ();
1124 /* We have futzed with the layering of canvas items on our streamviews.
1125 If any region changed layer, this will have resulted in the stream
1126 views being asked to set up their region views, and all will be well.
1127 If not, we might now have badly-ordered region views. Ask the StreamViews
1128 involved to sort themselves out, just in case.
1131 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1132 (*i)->view()->playlist_layered ((*i)->track ());
1136 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1137 * @param region Region to remove.
1138 * @param playlist playlist To remove from.
1139 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1140 * that clear_changes () is only called once per playlist.
1143 RegionMoveDrag::remove_region_from_playlist (
1144 boost::shared_ptr<Region> region,
1145 boost::shared_ptr<Playlist> playlist,
1146 PlaylistSet& modified_playlists
1149 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1152 playlist->clear_changes ();
1155 playlist->remove_region (region);
1159 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1160 * clearing the playlist's diff history first if necessary.
1161 * @param region Region to insert.
1162 * @param dest_rtv Destination RouteTimeAxisView.
1163 * @param dest_layer Destination layer.
1164 * @param where Destination position.
1165 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1166 * that clear_changes () is only called once per playlist.
1167 * @return New RegionView, or 0 if no insert was performed.
1170 RegionMoveDrag::insert_region_into_playlist (
1171 boost::shared_ptr<Region> region,
1172 RouteTimeAxisView* dest_rtv,
1175 PlaylistSet& modified_playlists
1178 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1179 if (!dest_playlist) {
1183 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1184 _new_region_view = 0;
1185 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1187 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1188 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1190 dest_playlist->clear_changes ();
1193 dest_playlist->add_region (region, where);
1195 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1196 dest_playlist->set_layer (region, dest_layer);
1201 assert (_new_region_view);
1203 return _new_region_view;
1207 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1209 _new_region_view = rv;
1213 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1215 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1216 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1218 _editor->session()->add_command (c);
1227 RegionMoveDrag::aborted (bool movement_occurred)
1231 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1238 RegionMotionDrag::aborted (movement_occurred);
1243 RegionMotionDrag::aborted (bool)
1245 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1246 if ((*i)->view()->layer_display() == Expanded) {
1247 (*i)->view()->set_layer_display (Stacked);
1251 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1252 RegionView* rv = i->view;
1253 TimeAxisView* tv = &(rv->get_time_axis_view ());
1254 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1256 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1257 rv->get_canvas_group()->property_y() = 0;
1258 rv->get_time_axis_view().reveal_dependent_views (*rv);
1259 rv->fake_set_opaque (false);
1260 rv->move (-_total_x_delta, 0);
1261 rv->set_height (rtv->view()->child_height ());
1264 _editor->update_canvas_now ();
1267 /** @param b true to brush, otherwise false.
1268 * @param c true to make copies of the regions being moved, otherwise false.
1270 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1271 : RegionMotionDrag (e, i, p, v, b),
1274 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1277 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1278 if (rtv && rtv->is_track()) {
1279 speed = rtv->track()->speed ();
1282 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1286 RegionMoveDrag::setup_pointer_frame_offset ()
1288 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1291 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1292 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1294 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1296 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1297 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1299 _primary = v->view()->create_region_view (r, false, false);
1301 _primary->get_canvas_group()->show ();
1302 _primary->set_position (pos, 0);
1303 _views.push_back (DraggingView (_primary, this));
1305 _last_frame_position = pos;
1307 _item = _primary->get_canvas_group ();
1311 RegionInsertDrag::finished (GdkEvent *, bool)
1313 _editor->update_canvas_now ();
1315 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1317 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1318 _primary->get_canvas_group()->property_y() = 0;
1320 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1322 _editor->begin_reversible_command (Operations::insert_region);
1323 playlist->clear_changes ();
1324 playlist->add_region (_primary->region (), _last_frame_position);
1325 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1326 _editor->commit_reversible_command ();
1334 RegionInsertDrag::aborted (bool)
1341 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1342 : RegionMoveDrag (e, i, p, v, false, false)
1344 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1347 struct RegionSelectionByPosition {
1348 bool operator() (RegionView*a, RegionView* b) {
1349 return a->region()->position () < b->region()->position();
1354 RegionSpliceDrag::motion (GdkEvent* event, bool)
1356 /* Which trackview is this ? */
1358 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1359 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1361 /* The region motion is only processed if the pointer is over
1365 if (!tv || !tv->is_track()) {
1366 /* To make sure we hide the verbose canvas cursor when the mouse is
1367 not held over and audiotrack.
1369 _editor->verbose_cursor()->hide ();
1375 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1381 RegionSelection copy (_editor->selection->regions);
1383 RegionSelectionByPosition cmp;
1386 framepos_t const pf = adjusted_current_frame (event);
1388 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1390 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1396 boost::shared_ptr<Playlist> playlist;
1398 if ((playlist = atv->playlist()) == 0) {
1402 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1407 if (pf < (*i)->region()->last_frame() + 1) {
1411 if (pf > (*i)->region()->first_frame()) {
1417 playlist->shuffle ((*i)->region(), dir);
1422 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1424 RegionMoveDrag::finished (event, movement_occurred);
1428 RegionSpliceDrag::aborted (bool)
1433 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1435 _view (dynamic_cast<MidiTimeAxisView*> (v))
1437 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1443 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1447 _view->playlist()->freeze ();
1450 framepos_t const f = adjusted_current_frame (event);
1451 if (f < grab_frame()) {
1452 _region->set_position (f);
1455 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1456 so that if this region is duplicated, its duplicate starts on
1457 a snap point rather than 1 frame after a snap point. Otherwise things get
1458 a bit confusing as if a region starts 1 frame after a snap point, one cannot
1459 place snapped notes at the start of the region.
1462 framecnt_t const len = abs (f - grab_frame () - 1);
1463 _region->set_length (len < 1 ? 1 : len);
1469 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1471 if (!movement_occurred) {
1474 _view->playlist()->thaw ();
1478 _editor->commit_reversible_command ();
1483 RegionCreateDrag::add_region ()
1485 if (_editor->session()) {
1486 const TempoMap& map (_editor->session()->tempo_map());
1487 framecnt_t pos = grab_frame();
1488 const Meter& m = map.meter_at (pos);
1489 /* not that the frame rate used here can be affected by pull up/down which
1492 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1493 _region = _view->add_region (grab_frame(), len, false);
1498 RegionCreateDrag::aborted (bool)
1501 _view->playlist()->thaw ();
1507 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1511 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1515 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1517 Gdk::Cursor* cursor;
1518 ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1519 float x_fraction = cnote->mouse_x_fraction ();
1521 if (x_fraction > 0.0 && x_fraction < 0.25) {
1522 cursor = _editor->cursors()->left_side_trim;
1524 cursor = _editor->cursors()->right_side_trim;
1527 Drag::start_grab (event, cursor);
1529 region = &cnote->region_view();
1531 double const region_start = region->get_position_pixels();
1532 double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1534 if (grab_x() <= middle_point) {
1535 cursor = _editor->cursors()->left_side_trim;
1538 cursor = _editor->cursors()->right_side_trim;
1542 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1544 if (event->motion.state & Keyboard::PrimaryModifier) {
1550 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1552 if (ms.size() > 1) {
1553 /* has to be relative, may make no sense otherwise */
1557 /* select this note; if it is already selected, preserve the existing selection,
1558 otherwise make this note the only one selected.
1560 region->note_selected (cnote, cnote->selected ());
1562 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1563 MidiRegionSelection::iterator next;
1566 (*r)->begin_resizing (at_front);
1572 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1574 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1575 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1576 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1581 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1583 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1584 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1585 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1590 NoteResizeDrag::aborted (bool)
1592 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1593 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1594 (*r)->abort_resizing ();
1598 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1601 DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1605 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1611 RegionGainDrag::finished (GdkEvent *, bool)
1617 RegionGainDrag::aborted (bool)
1622 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1623 : RegionDrag (e, i, p, v)
1625 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1629 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1632 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1633 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1635 if (tv && tv->is_track()) {
1636 speed = tv->track()->speed();
1639 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1640 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1641 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1643 framepos_t const pf = adjusted_current_frame (event);
1645 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1646 /* Move the contents of the region around without changing the region bounds */
1647 _operation = ContentsTrim;
1648 Drag::start_grab (event, _editor->cursors()->trimmer);
1650 /* These will get overridden for a point trim.*/
1651 if (pf < (region_start + region_length/2)) {
1652 /* closer to front */
1653 _operation = StartTrim;
1654 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1657 _operation = EndTrim;
1658 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1662 switch (_operation) {
1664 show_verbose_cursor_time (region_start);
1665 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1666 i->view->trim_front_starting ();
1670 show_verbose_cursor_time (region_end);
1673 show_verbose_cursor_time (pf);
1677 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1678 i->view->region()->suspend_property_changes ();
1683 TrimDrag::motion (GdkEvent* event, bool first_move)
1685 RegionView* rv = _primary;
1688 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1689 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1690 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1692 if (tv && tv->is_track()) {
1693 speed = tv->track()->speed();
1696 framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1702 switch (_operation) {
1704 trim_type = "Region start trim";
1707 trim_type = "Region end trim";
1710 trim_type = "Region content trim";
1714 _editor->begin_reversible_command (trim_type);
1716 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1717 RegionView* rv = i->view;
1718 rv->fake_set_opaque (false);
1719 rv->enable_display (false);
1720 rv->region()->playlist()->clear_owned_changes ();
1722 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1725 arv->temporarily_hide_envelope ();
1728 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1729 insert_result = _editor->motion_frozen_playlists.insert (pl);
1731 if (insert_result.second) {
1737 bool non_overlap_trim = false;
1739 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1740 non_overlap_trim = true;
1743 switch (_operation) {
1745 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1746 i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1751 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1752 i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1758 bool swap_direction = false;
1760 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1761 swap_direction = true;
1764 framecnt_t frame_delta = 0;
1766 bool left_direction = false;
1767 if (last_pointer_frame() > adjusted_current_frame(event)) {
1768 left_direction = true;
1771 if (left_direction) {
1772 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1774 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1777 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1778 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1784 switch (_operation) {
1786 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1789 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1792 show_verbose_cursor_time (adjusted_current_frame (event));
1799 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1801 if (movement_occurred) {
1802 motion (event, false);
1804 /* This must happen before the region's StatefulDiffCommand is created, as it may
1805 `correct' (ahem) the region's _start from being negative to being zero. It
1806 needs to be zero in the undo record.
1808 if (_operation == StartTrim) {
1809 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1810 i->view->trim_front_ending ();
1814 if (!_editor->selection->selected (_primary)) {
1815 _primary->thaw_after_trim ();
1818 set<boost::shared_ptr<Playlist> > diffed_playlists;
1820 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1821 i->view->thaw_after_trim ();
1822 i->view->enable_display (true);
1823 i->view->fake_set_opaque (true);
1825 /* Trimming one region may affect others on the playlist, so we need
1826 to get undo Commands from the whole playlist rather than just the
1827 region. Use diffed_playlists to make sure we don't diff a given
1828 playlist more than once.
1830 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1831 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1832 vector<Command*> cmds;
1834 _editor->session()->add_commands (cmds);
1835 diffed_playlists.insert (p);
1839 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1843 _editor->motion_frozen_playlists.clear ();
1844 _editor->commit_reversible_command();
1847 /* no mouse movement */
1848 _editor->point_trim (event, adjusted_current_frame (event));
1851 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1852 if (_operation == StartTrim) {
1853 i->view->trim_front_ending ();
1856 i->view->region()->resume_property_changes ();
1861 TrimDrag::aborted (bool movement_occurred)
1863 /* Our motion method is changing model state, so use the Undo system
1864 to cancel. Perhaps not ideal, as this will leave an Undo point
1865 behind which may be slightly odd from the user's point of view.
1870 if (movement_occurred) {
1874 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1875 i->view->region()->resume_property_changes ();
1880 TrimDrag::setup_pointer_frame_offset ()
1882 list<DraggingView>::iterator i = _views.begin ();
1883 while (i != _views.end() && i->view != _primary) {
1887 if (i == _views.end()) {
1891 switch (_operation) {
1893 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1896 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1903 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1907 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1908 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1913 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1915 Drag::start_grab (event, cursor);
1916 show_verbose_cursor_time (adjusted_current_frame(event));
1920 MeterMarkerDrag::setup_pointer_frame_offset ()
1922 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1926 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
1930 // create a dummy marker for visual representation of moving the
1931 // section, because whether its a copy or not, we're going to
1932 // leave or lose the original marker (leave if its a copy; lose if its
1933 // not, because we'll remove it from the map).
1935 MeterSection section (_marker->meter());
1937 if (!section.movable()) {
1942 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
1944 _marker = new MeterMarker (
1946 *_editor->meter_group,
1947 ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1949 *new MeterSection (_marker->meter())
1952 /* use the new marker for the grab */
1953 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
1956 TempoMap& map (_editor->session()->tempo_map());
1957 /* get current state */
1958 before_state = &map.get_state();
1959 /* remove the section while we drag it */
1960 map.remove_meter (section, true);
1964 framepos_t const pf = adjusted_current_frame (event);
1965 _marker->set_position (pf);
1966 show_verbose_cursor_time (pf);
1970 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1972 if (!movement_occurred) {
1976 motion (event, false);
1978 Timecode::BBT_Time when;
1980 TempoMap& map (_editor->session()->tempo_map());
1981 map.bbt_time (last_pointer_frame(), when);
1983 if (_copy == true) {
1984 _editor->begin_reversible_command (_("copy meter mark"));
1985 XMLNode &before = map.get_state();
1986 map.add_meter (_marker->meter(), when);
1987 XMLNode &after = map.get_state();
1988 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1989 _editor->commit_reversible_command ();
1992 _editor->begin_reversible_command (_("move meter mark"));
1994 /* we removed it before, so add it back now */
1996 map.add_meter (_marker->meter(), when);
1997 XMLNode &after = map.get_state();
1998 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
1999 _editor->commit_reversible_command ();
2002 // delete the dummy marker we used for visual representation while moving.
2003 // a new visual marker will show up automatically.
2008 MeterMarkerDrag::aborted (bool moved)
2010 _marker->set_position (_marker->meter().frame ());
2013 TempoMap& map (_editor->session()->tempo_map());
2014 /* we removed it before, so add it back now */
2015 map.add_meter (_marker->meter(), _marker->meter().frame());
2016 // delete the dummy marker we used for visual representation while moving.
2017 // a new visual marker will show up automatically.
2022 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2026 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2028 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2033 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2035 Drag::start_grab (event, cursor);
2036 show_verbose_cursor_time (adjusted_current_frame (event));
2040 TempoMarkerDrag::setup_pointer_frame_offset ()
2042 _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2046 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2050 // create a dummy marker for visual representation of moving the
2051 // section, because whether its a copy or not, we're going to
2052 // leave or lose the original marker (leave if its a copy; lose if its
2053 // not, because we'll remove it from the map).
2055 // create a dummy marker for visual representation of moving the copy.
2056 // The actual copying is not done before we reach the finish callback.
2059 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2061 TempoSection section (_marker->tempo());
2063 _marker = new TempoMarker (
2065 *_editor->tempo_group,
2066 ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
2068 *new TempoSection (_marker->tempo())
2071 /* use the new marker for the grab */
2072 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2075 TempoMap& map (_editor->session()->tempo_map());
2076 /* get current state */
2077 before_state = &map.get_state();
2078 /* remove the section while we drag it */
2079 map.remove_tempo (section, true);
2083 framepos_t const pf = adjusted_current_frame (event);
2084 _marker->set_position (pf);
2085 show_verbose_cursor_time (pf);
2089 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2091 if (!movement_occurred) {
2095 motion (event, false);
2097 TempoMap& map (_editor->session()->tempo_map());
2098 framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2099 Timecode::BBT_Time when;
2101 map.bbt_time (beat_time, when);
2103 if (_copy == true) {
2104 _editor->begin_reversible_command (_("copy tempo mark"));
2105 XMLNode &before = map.get_state();
2106 map.add_tempo (_marker->tempo(), when);
2107 XMLNode &after = map.get_state();
2108 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2109 _editor->commit_reversible_command ();
2112 _editor->begin_reversible_command (_("move tempo mark"));
2113 /* we removed it before, so add it back now */
2114 map.add_tempo (_marker->tempo(), when);
2115 XMLNode &after = map.get_state();
2116 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2117 _editor->commit_reversible_command ();
2120 // delete the dummy marker we used for visual representation while moving.
2121 // a new visual marker will show up automatically.
2126 TempoMarkerDrag::aborted (bool moved)
2128 _marker->set_position (_marker->tempo().frame());
2130 TempoMap& map (_editor->session()->tempo_map());
2131 /* we removed it before, so add it back now */
2132 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2133 // delete the dummy marker we used for visual representation while moving.
2134 // a new visual marker will show up automatically.
2139 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2143 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2146 /** Do all the things we do when dragging the playhead to make it look as though
2147 * we have located, without actually doing the locate (because that would cause
2148 * the diskstream buffers to be refilled, which is too slow).
2151 CursorDrag::fake_locate (framepos_t t)
2153 _editor->playhead_cursor->set_position (t);
2155 Session* s = _editor->session ();
2156 if (s->timecode_transmission_suspended ()) {
2157 framepos_t const f = _editor->playhead_cursor->current_frame;
2158 s->send_mmc_locate (f);
2159 s->send_full_time_code (f);
2162 show_verbose_cursor_time (t);
2163 _editor->UpdateAllTransportClocks (t);
2167 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2169 Drag::start_grab (event, c);
2171 _grab_zoom = _editor->frames_per_unit;
2173 framepos_t where = _editor->event_frame (event, 0, 0);
2174 _editor->snap_to_with_modifier (where, event);
2176 _editor->_dragging_playhead = true;
2178 Session* s = _editor->session ();
2181 if (_was_rolling && _stop) {
2185 if (s->is_auditioning()) {
2186 s->cancel_audition ();
2189 s->request_suspend_timecode_transmission ();
2190 while (!s->timecode_transmission_suspended ()) {
2191 /* twiddle our thumbs */
2195 fake_locate (where);
2199 CursorDrag::motion (GdkEvent* event, bool)
2201 framepos_t const adjusted_frame = adjusted_current_frame (event);
2202 if (adjusted_frame != last_pointer_frame()) {
2203 fake_locate (adjusted_frame);
2205 _editor->update_canvas_now ();
2211 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2213 _editor->_dragging_playhead = false;
2215 if (!movement_occurred && _stop) {
2219 motion (event, false);
2221 Session* s = _editor->session ();
2223 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2224 _editor->_pending_locate_request = true;
2225 s->request_resume_timecode_transmission ();
2230 CursorDrag::aborted (bool)
2232 if (_editor->_dragging_playhead) {
2233 _editor->session()->request_resume_timecode_transmission ();
2234 _editor->_dragging_playhead = false;
2237 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2240 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2241 : RegionDrag (e, i, p, v)
2243 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2247 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2249 Drag::start_grab (event, cursor);
2251 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2252 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2254 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2256 arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2260 FadeInDrag::setup_pointer_frame_offset ()
2262 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2263 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2264 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2268 FadeInDrag::motion (GdkEvent* event, bool)
2270 framecnt_t fade_length;
2272 framepos_t const pos = adjusted_current_frame (event);
2274 boost::shared_ptr<Region> region = _primary->region ();
2276 if (pos < (region->position() + 64)) {
2277 fade_length = 64; // this should be a minimum defined somewhere
2278 } else if (pos > region->last_frame()) {
2279 fade_length = region->length();
2281 fade_length = pos - region->position();
2284 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2286 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2292 tmp->reset_fade_in_shape_width (fade_length);
2293 tmp->show_fade_line((framecnt_t) fade_length);
2296 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2300 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2302 if (!movement_occurred) {
2306 framecnt_t fade_length;
2308 framepos_t const pos = adjusted_current_frame (event);
2310 boost::shared_ptr<Region> region = _primary->region ();
2312 if (pos < (region->position() + 64)) {
2313 fade_length = 64; // this should be a minimum defined somewhere
2314 } else if (pos > region->last_frame()) {
2315 fade_length = region->length();
2317 fade_length = pos - region->position();
2320 _editor->begin_reversible_command (_("change fade in length"));
2322 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2324 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2330 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2331 XMLNode &before = alist->get_state();
2333 tmp->audio_region()->set_fade_in_length (fade_length);
2334 tmp->audio_region()->set_fade_in_active (true);
2335 tmp->hide_fade_line();
2337 XMLNode &after = alist->get_state();
2338 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2341 _editor->commit_reversible_command ();
2345 FadeInDrag::aborted (bool)
2347 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2348 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2354 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2355 tmp->hide_fade_line();
2359 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2360 : RegionDrag (e, i, p, v)
2362 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2366 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2368 Drag::start_grab (event, cursor);
2370 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2371 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2373 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2375 arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2379 FadeOutDrag::setup_pointer_frame_offset ()
2381 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2382 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2383 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2387 FadeOutDrag::motion (GdkEvent* event, bool)
2389 framecnt_t fade_length;
2391 framepos_t const pos = adjusted_current_frame (event);
2393 boost::shared_ptr<Region> region = _primary->region ();
2395 if (pos > (region->last_frame() - 64)) {
2396 fade_length = 64; // this should really be a minimum fade defined somewhere
2398 else if (pos < region->position()) {
2399 fade_length = region->length();
2402 fade_length = region->last_frame() - pos;
2405 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2407 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2413 tmp->reset_fade_out_shape_width (fade_length);
2414 tmp->show_fade_line(region->length() - fade_length);
2417 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2421 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2423 if (!movement_occurred) {
2427 framecnt_t fade_length;
2429 framepos_t const pos = adjusted_current_frame (event);
2431 boost::shared_ptr<Region> region = _primary->region ();
2433 if (pos > (region->last_frame() - 64)) {
2434 fade_length = 64; // this should really be a minimum fade defined somewhere
2436 else if (pos < region->position()) {
2437 fade_length = region->length();
2440 fade_length = region->last_frame() - pos;
2443 _editor->begin_reversible_command (_("change fade out length"));
2445 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2447 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2453 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2454 XMLNode &before = alist->get_state();
2456 tmp->audio_region()->set_fade_out_length (fade_length);
2457 tmp->audio_region()->set_fade_out_active (true);
2458 tmp->hide_fade_line();
2460 XMLNode &after = alist->get_state();
2461 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2464 _editor->commit_reversible_command ();
2468 FadeOutDrag::aborted (bool)
2470 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2471 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2477 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2478 tmp->hide_fade_line();
2482 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2485 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2487 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2490 _points.push_back (Gnome::Art::Point (0, 0));
2491 _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2494 MarkerDrag::~MarkerDrag ()
2496 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2502 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2504 Drag::start_grab (event, cursor);
2508 Location *location = _editor->find_location_from_marker (_marker, is_start);
2509 _editor->_dragging_edit_point = true;
2511 update_item (location);
2513 // _drag_line->show();
2514 // _line->raise_to_top();
2517 show_verbose_cursor_time (location->start());
2519 show_verbose_cursor_time (location->end());
2522 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2525 case Selection::Toggle:
2526 _editor->selection->toggle (_marker);
2528 case Selection::Set:
2529 if (!_editor->selection->selected (_marker)) {
2530 _editor->selection->set (_marker);
2533 case Selection::Extend:
2535 Locations::LocationList ll;
2536 list<Marker*> to_add;
2538 _editor->selection->markers.range (s, e);
2539 s = min (_marker->position(), s);
2540 e = max (_marker->position(), e);
2543 if (e < max_framepos) {
2546 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2547 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2548 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2551 to_add.push_back (lm->start);
2554 to_add.push_back (lm->end);
2558 if (!to_add.empty()) {
2559 _editor->selection->add (to_add);
2563 case Selection::Add:
2564 _editor->selection->add (_marker);
2568 /* Set up copies for us to manipulate during the drag */
2570 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2571 Location* l = _editor->find_location_from_marker (*i, is_start);
2572 _copied_locations.push_back (new Location (*l));
2577 MarkerDrag::setup_pointer_frame_offset ()
2580 Location *location = _editor->find_location_from_marker (_marker, is_start);
2581 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2585 MarkerDrag::motion (GdkEvent* event, bool)
2587 framecnt_t f_delta = 0;
2589 bool move_both = false;
2591 Location *real_location;
2592 Location *copy_location = 0;
2594 framepos_t const newframe = adjusted_current_frame (event);
2596 framepos_t next = newframe;
2598 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2602 MarkerSelection::iterator i;
2603 list<Location*>::iterator x;
2605 /* find the marker we're dragging, and compute the delta */
2607 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2608 x != _copied_locations.end() && i != _editor->selection->markers.end();
2614 if (marker == _marker) {
2616 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2621 if (real_location->is_mark()) {
2622 f_delta = newframe - copy_location->start();
2626 switch (marker->type()) {
2627 case Marker::SessionStart:
2628 case Marker::RangeStart:
2629 case Marker::LoopStart:
2630 case Marker::PunchIn:
2631 f_delta = newframe - copy_location->start();
2634 case Marker::SessionEnd:
2635 case Marker::RangeEnd:
2636 case Marker::LoopEnd:
2637 case Marker::PunchOut:
2638 f_delta = newframe - copy_location->end();
2641 /* what kind of marker is this ? */
2649 if (i == _editor->selection->markers.end()) {
2650 /* hmm, impossible - we didn't find the dragged marker */
2654 /* now move them all */
2656 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2657 x != _copied_locations.end() && i != _editor->selection->markers.end();
2663 /* call this to find out if its the start or end */
2665 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2669 if (real_location->locked()) {
2673 if (copy_location->is_mark()) {
2677 copy_location->set_start (copy_location->start() + f_delta);
2681 framepos_t new_start = copy_location->start() + f_delta;
2682 framepos_t new_end = copy_location->end() + f_delta;
2684 if (is_start) { // start-of-range marker
2687 copy_location->set_start (new_start);
2688 copy_location->set_end (new_end);
2689 } else if (new_start < copy_location->end()) {
2690 copy_location->set_start (new_start);
2691 } else if (newframe > 0) {
2692 _editor->snap_to (next, 1, true);
2693 copy_location->set_end (next);
2694 copy_location->set_start (newframe);
2697 } else { // end marker
2700 copy_location->set_end (new_end);
2701 copy_location->set_start (new_start);
2702 } else if (new_end > copy_location->start()) {
2703 copy_location->set_end (new_end);
2704 } else if (newframe > 0) {
2705 _editor->snap_to (next, -1, true);
2706 copy_location->set_start (next);
2707 copy_location->set_end (newframe);
2712 update_item (copy_location);
2714 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2717 lm->set_position (copy_location->start(), copy_location->end());
2721 assert (!_copied_locations.empty());
2723 show_verbose_cursor_time (newframe);
2726 _editor->update_canvas_now ();
2731 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2733 if (!movement_occurred) {
2735 /* just a click, do nothing but finish
2736 off the selection process
2739 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2742 case Selection::Set:
2743 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2744 _editor->selection->set (_marker);
2748 case Selection::Toggle:
2749 case Selection::Extend:
2750 case Selection::Add:
2757 _editor->_dragging_edit_point = false;
2759 _editor->begin_reversible_command ( _("move marker") );
2760 XMLNode &before = _editor->session()->locations()->get_state();
2762 MarkerSelection::iterator i;
2763 list<Location*>::iterator x;
2766 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2767 x != _copied_locations.end() && i != _editor->selection->markers.end();
2770 Location * location = _editor->find_location_from_marker (*i, is_start);
2774 if (location->locked()) {
2778 if (location->is_mark()) {
2779 location->set_start ((*x)->start());
2781 location->set ((*x)->start(), (*x)->end());
2786 XMLNode &after = _editor->session()->locations()->get_state();
2787 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2788 _editor->commit_reversible_command ();
2792 MarkerDrag::aborted (bool)
2798 MarkerDrag::update_item (Location*)
2803 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2805 _cumulative_x_drag (0),
2806 _cumulative_y_drag (0)
2808 if (_zero_gain_fraction < 0.0) {
2809 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
2812 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2814 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2820 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2822 Drag::start_grab (event, _editor->cursors()->fader);
2824 // start the grab at the center of the control point so
2825 // the point doesn't 'jump' to the mouse after the first drag
2826 _fixed_grab_x = _point->get_x();
2827 _fixed_grab_y = _point->get_y();
2829 float const fraction = 1 - (_point->get_y() / _point->line().height());
2831 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2833 _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
2834 event->button.x + 10, event->button.y + 10);
2836 _editor->verbose_cursor()->show ();
2840 ControlPointDrag::motion (GdkEvent* event, bool)
2842 double dx = _drags->current_pointer_x() - last_pointer_x();
2843 double dy = _drags->current_pointer_y() - last_pointer_y();
2845 if (event->button.state & Keyboard::SecondaryModifier) {
2850 /* coordinate in pixels relative to the start of the region (for region-based automation)
2851 or track (for track-based automation) */
2852 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2853 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2855 // calculate zero crossing point. back off by .01 to stay on the
2856 // positive side of zero
2857 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2859 // make sure we hit zero when passing through
2860 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2864 if (_x_constrained) {
2867 if (_y_constrained) {
2871 _cumulative_x_drag = cx - _fixed_grab_x;
2872 _cumulative_y_drag = cy - _fixed_grab_y;
2876 cy = min ((double) _point->line().height(), cy);
2878 framepos_t cx_frames = _editor->unit_to_frame (cx);
2880 if (!_x_constrained) {
2881 _editor->snap_to_with_modifier (cx_frames, event);
2884 cx_frames = min (cx_frames, _point->line().maximum_time());
2886 float const fraction = 1.0 - (cy / _point->line().height());
2888 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2890 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2892 _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
2896 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2898 if (!movement_occurred) {
2902 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2903 _editor->reset_point_selection ();
2907 motion (event, false);
2910 _point->line().end_drag ();
2911 _editor->session()->commit_reversible_command ();
2915 ControlPointDrag::aborted (bool)
2917 _point->line().reset ();
2921 ControlPointDrag::active (Editing::MouseMode m)
2923 if (m == Editing::MouseGain) {
2924 /* always active in mouse gain */
2928 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2929 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2932 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2935 _cumulative_y_drag (0)
2937 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2941 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2943 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2946 _item = &_line->grab_item ();
2948 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2949 origin, and ditto for y.
2952 double cx = event->button.x;
2953 double cy = event->button.y;
2955 _line->parent_group().w2i (cx, cy);
2957 framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2962 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2963 /* no adjacent points */
2967 Drag::start_grab (event, _editor->cursors()->fader);
2969 /* store grab start in parent frame */
2974 double fraction = 1.0 - (cy / _line->height());
2976 _line->start_drag_line (before, after, fraction);
2978 _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
2979 event->button.x + 10, event->button.y + 10);
2981 _editor->verbose_cursor()->show ();
2985 LineDrag::motion (GdkEvent* event, bool)
2987 double dy = _drags->current_pointer_y() - last_pointer_y();
2989 if (event->button.state & Keyboard::SecondaryModifier) {
2993 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2995 _cumulative_y_drag = cy - _fixed_grab_y;
2998 cy = min ((double) _line->height(), cy);
3000 double const fraction = 1.0 - (cy / _line->height());
3004 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
3010 /* we are ignoring x position for this drag, so we can just pass in anything */
3011 _line->drag_motion (0, fraction, true, push);
3013 _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3017 LineDrag::finished (GdkEvent* event, bool)
3019 motion (event, false);
3021 _editor->session()->commit_reversible_command ();
3025 LineDrag::aborted (bool)
3030 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3033 _cumulative_x_drag (0)
3035 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3039 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3041 Drag::start_grab (event);
3043 _line = reinterpret_cast<Line*> (_item);
3046 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3048 double cx = event->button.x;
3049 double cy = event->button.y;
3051 _item->property_parent().get_value()->w2i(cx, cy);
3053 /* store grab start in parent frame */
3054 _region_view_grab_x = cx;
3056 _before = *(float*) _item->get_data ("position");
3058 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3060 _max_x = _editor->frame_to_pixel(_arv->get_duration());
3064 FeatureLineDrag::motion (GdkEvent*, bool)
3066 double dx = _drags->current_pointer_x() - last_pointer_x();
3068 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3070 _cumulative_x_drag += dx;
3072 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3081 ArdourCanvas::Points points;
3083 double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
3085 _line->get_bounds(x1, y2, x2, y2);
3087 points.push_back(Gnome::Art::Point(cx, 2.0)); // first x-coord needs to be a non-normal value
3088 points.push_back(Gnome::Art::Point(cx, y2 - y1));
3090 _line->property_points() = points;
3092 float *pos = new float;
3095 _line->set_data ("position", pos);
3101 FeatureLineDrag::finished (GdkEvent*, bool)
3103 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3104 _arv->update_transient(_before, _before);
3108 FeatureLineDrag::aborted (bool)
3113 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3115 , _vertical_only (false)
3117 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3121 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3123 Drag::start_grab (event);
3124 show_verbose_cursor_time (adjusted_current_frame (event));
3128 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3135 framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3137 framepos_t grab = grab_frame ();
3138 if (Config->get_rubberbanding_snaps_to_grid ()) {
3139 _editor->snap_to_with_modifier (grab, event);
3142 /* base start and end on initial click position */
3152 if (_drags->current_pointer_y() < grab_y()) {
3153 y1 = _drags->current_pointer_y();
3156 y2 = _drags->current_pointer_y();
3161 if (start != end || y1 != y2) {
3163 double x1 = _editor->frame_to_pixel (start);
3164 double x2 = _editor->frame_to_pixel (end);
3166 _editor->rubberband_rect->property_x1() = x1;
3167 if (_vertical_only) {
3168 /* fixed 10 pixel width */
3169 _editor->rubberband_rect->property_x2() = x1 + 10;
3171 _editor->rubberband_rect->property_x2() = x2;
3174 _editor->rubberband_rect->property_y1() = y1;
3175 _editor->rubberband_rect->property_y2() = y2;
3177 _editor->rubberband_rect->show();
3178 _editor->rubberband_rect->raise_to_top();
3180 show_verbose_cursor_time (pf);
3182 do_select_things (event, true);
3187 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3192 if (grab_frame() < last_pointer_frame()) {
3194 x2 = last_pointer_frame ();
3197 x1 = last_pointer_frame ();
3203 if (_drags->current_pointer_y() < grab_y()) {
3204 y1 = _drags->current_pointer_y();
3207 y2 = _drags->current_pointer_y();
3211 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3215 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3217 if (movement_occurred) {
3219 motion (event, false);
3220 do_select_things (event, false);
3228 _editor->rubberband_rect->hide();
3232 RubberbandSelectDrag::aborted (bool)
3234 _editor->rubberband_rect->hide ();
3237 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3238 : RegionDrag (e, i, p, v)
3240 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3244 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3246 Drag::start_grab (event, cursor);
3248 show_verbose_cursor_time (adjusted_current_frame (event));
3252 TimeFXDrag::motion (GdkEvent* event, bool)
3254 RegionView* rv = _primary;
3256 framepos_t const pf = adjusted_current_frame (event);
3258 if (pf > rv->region()->position()) {
3259 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3262 show_verbose_cursor_time (pf);
3266 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3268 _primary->get_time_axis_view().hide_timestretch ();
3270 if (!movement_occurred) {
3274 if (last_pointer_frame() < _primary->region()->position()) {
3275 /* backwards drag of the left edge - not usable */
3279 framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3281 float percentage = (double) newlen / (double) _primary->region()->length();
3283 #ifndef USE_RUBBERBAND
3284 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3285 if (_primary->region()->data_type() == DataType::AUDIO) {
3286 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3290 // XXX how do timeFX on multiple regions ?
3295 RegionSelection all = _editor->get_equivalent_regions (rs, ARDOUR::Properties::edit.property_id);
3297 if (_editor->time_stretch (all, percentage) == -1) {
3298 error << _("An error occurred while executing time stretch operation") << endmsg;
3303 TimeFXDrag::aborted (bool)
3305 _primary->get_time_axis_view().hide_timestretch ();
3308 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3311 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3315 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3317 Drag::start_grab (event);
3321 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3323 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3327 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3329 if (movement_occurred && _editor->session()) {
3330 /* make sure we stop */
3331 _editor->session()->request_transport_speed (0.0);
3336 ScrubDrag::aborted (bool)
3341 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3345 , _original_pointer_time_axis (-1)
3346 , _last_pointer_time_axis (-1)
3348 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3352 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3354 if (_editor->session() == 0) {
3358 Gdk::Cursor* cursor = 0;
3360 switch (_operation) {
3361 case CreateSelection:
3362 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3367 cursor = _editor->cursors()->selector;
3368 Drag::start_grab (event, cursor);
3371 case SelectionStartTrim:
3372 if (_editor->clicked_axisview) {
3373 _editor->clicked_axisview->order_selection_trims (_item, true);
3375 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3378 case SelectionEndTrim:
3379 if (_editor->clicked_axisview) {
3380 _editor->clicked_axisview->order_selection_trims (_item, false);
3382 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3386 Drag::start_grab (event, cursor);
3390 if (_operation == SelectionMove) {
3391 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3393 show_verbose_cursor_time (adjusted_current_frame (event));
3396 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3400 SelectionDrag::setup_pointer_frame_offset ()
3402 switch (_operation) {
3403 case CreateSelection:
3404 _pointer_frame_offset = 0;
3407 case SelectionStartTrim:
3409 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3412 case SelectionEndTrim:
3413 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3419 SelectionDrag::motion (GdkEvent* event, bool first_move)
3421 framepos_t start = 0;
3425 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3426 if (pending_time_axis.first == 0) {
3430 framepos_t const pending_position = adjusted_current_frame (event);
3432 /* only alter selection if things have changed */
3434 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3438 switch (_operation) {
3439 case CreateSelection:
3441 framepos_t grab = grab_frame ();
3444 _editor->snap_to (grab);
3447 if (pending_position < grab_frame()) {
3448 start = pending_position;
3451 end = pending_position;
3455 /* first drag: Either add to the selection
3456 or create a new selection
3462 /* adding to the selection */
3463 _editor->set_selected_track_as_side_effect (Selection::Add);
3464 //_editor->selection->add (_editor->clicked_axisview);
3465 _editor->clicked_selection = _editor->selection->add (start, end);
3470 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3471 //_editor->selection->set (_editor->clicked_axisview);
3472 _editor->set_selected_track_as_side_effect (Selection::Set);
3475 _editor->clicked_selection = _editor->selection->set (start, end);
3479 /* select the track that we're in */
3480 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3481 // _editor->set_selected_track_as_side_effect (Selection::Add);
3482 _editor->selection->add (pending_time_axis.first);
3483 _added_time_axes.push_back (pending_time_axis.first);
3486 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3487 tracks that we selected in the first place.
3490 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3491 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3493 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3494 while (i != _added_time_axes.end()) {
3496 list<TimeAxisView*>::iterator tmp = i;
3499 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3500 _editor->selection->remove (*i);
3501 _added_time_axes.remove (*i);
3510 case SelectionStartTrim:
3512 start = _editor->selection->time[_editor->clicked_selection].start;
3513 end = _editor->selection->time[_editor->clicked_selection].end;
3515 if (pending_position > end) {
3518 start = pending_position;
3522 case SelectionEndTrim:
3524 start = _editor->selection->time[_editor->clicked_selection].start;
3525 end = _editor->selection->time[_editor->clicked_selection].end;
3527 if (pending_position < start) {
3530 end = pending_position;
3537 start = _editor->selection->time[_editor->clicked_selection].start;
3538 end = _editor->selection->time[_editor->clicked_selection].end;
3540 length = end - start;
3542 start = pending_position;
3543 _editor->snap_to (start);
3545 end = start + length;
3550 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3551 _editor->start_canvas_autoscroll (1, 0);
3555 _editor->selection->replace (_editor->clicked_selection, start, end);
3558 if (_operation == SelectionMove) {
3559 show_verbose_cursor_time(start);
3561 show_verbose_cursor_time(pending_position);
3566 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3568 Session* s = _editor->session();
3570 if (movement_occurred) {
3571 motion (event, false);
3572 /* XXX this is not object-oriented programming at all. ick */
3573 if (_editor->selection->time.consolidate()) {
3574 _editor->selection->TimeChanged ();
3577 /* XXX what if its a music time selection? */
3578 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3579 s->request_play_range (&_editor->selection->time, true);
3584 /* just a click, no pointer movement.*/
3586 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3587 _editor->selection->clear_time();
3590 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3591 _editor->selection->set (_editor->clicked_axisview);
3594 if (s && s->get_play_range () && s->transport_rolling()) {
3595 s->request_stop (false, false);
3600 _editor->stop_canvas_autoscroll ();
3604 SelectionDrag::aborted (bool)
3609 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3614 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3616 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3617 physical_screen_height (_editor->get_window()));
3618 _drag_rect->hide ();
3620 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3621 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3625 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3627 if (_editor->session() == 0) {
3631 Gdk::Cursor* cursor = 0;
3633 if (!_editor->temp_location) {
3634 _editor->temp_location = new Location (*_editor->session());
3637 switch (_operation) {
3638 case CreateRangeMarker:
3639 case CreateTransportMarker:
3640 case CreateCDMarker:
3642 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3647 cursor = _editor->cursors()->selector;
3651 Drag::start_grab (event, cursor);
3653 show_verbose_cursor_time (adjusted_current_frame (event));
3657 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3659 framepos_t start = 0;
3661 ArdourCanvas::SimpleRect *crect;
3663 switch (_operation) {
3664 case CreateRangeMarker:
3665 crect = _editor->range_bar_drag_rect;
3667 case CreateTransportMarker:
3668 crect = _editor->transport_bar_drag_rect;
3670 case CreateCDMarker:
3671 crect = _editor->cd_marker_bar_drag_rect;
3674 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3679 framepos_t const pf = adjusted_current_frame (event);
3681 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3682 framepos_t grab = grab_frame ();
3683 _editor->snap_to (grab);
3685 if (pf < grab_frame()) {
3693 /* first drag: Either add to the selection
3694 or create a new selection.
3699 _editor->temp_location->set (start, end);
3703 update_item (_editor->temp_location);
3705 //_drag_rect->raise_to_top();
3710 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3711 _editor->start_canvas_autoscroll (1, 0);
3715 _editor->temp_location->set (start, end);
3717 double x1 = _editor->frame_to_pixel (start);
3718 double x2 = _editor->frame_to_pixel (end);
3719 crect->property_x1() = x1;
3720 crect->property_x2() = x2;
3722 update_item (_editor->temp_location);
3725 show_verbose_cursor_time (pf);
3730 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3732 Location * newloc = 0;
3736 if (movement_occurred) {
3737 motion (event, false);
3740 switch (_operation) {
3741 case CreateRangeMarker:
3742 case CreateCDMarker:
3744 _editor->begin_reversible_command (_("new range marker"));
3745 XMLNode &before = _editor->session()->locations()->get_state();
3746 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3747 if (_operation == CreateCDMarker) {
3748 flags = Location::IsRangeMarker | Location::IsCDMarker;
3749 _editor->cd_marker_bar_drag_rect->hide();
3752 flags = Location::IsRangeMarker;
3753 _editor->range_bar_drag_rect->hide();
3755 newloc = new Location (
3756 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3759 _editor->session()->locations()->add (newloc, true);
3760 XMLNode &after = _editor->session()->locations()->get_state();
3761 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3762 _editor->commit_reversible_command ();
3766 case CreateTransportMarker:
3767 // popup menu to pick loop or punch
3768 _editor->new_transport_marker_context_menu (&event->button, _item);
3772 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3774 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3779 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3781 if (end == max_framepos) {
3782 end = _editor->session()->current_end_frame ();
3785 if (start == max_framepos) {
3786 start = _editor->session()->current_start_frame ();
3789 switch (_editor->mouse_mode) {
3791 /* find the two markers on either side and then make the selection from it */
3792 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3796 /* find the two markers on either side of the click and make the range out of it */
3797 _editor->selection->set (start, end);
3806 _editor->stop_canvas_autoscroll ();
3810 RangeMarkerBarDrag::aborted (bool)
3816 RangeMarkerBarDrag::update_item (Location* location)
3818 double const x1 = _editor->frame_to_pixel (location->start());
3819 double const x2 = _editor->frame_to_pixel (location->end());
3821 _drag_rect->property_x1() = x1;
3822 _drag_rect->property_x2() = x2;
3825 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3829 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3833 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3835 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3836 Drag::start_grab (event, _editor->cursors()->zoom_out);
3839 Drag::start_grab (event, _editor->cursors()->zoom_in);
3843 show_verbose_cursor_time (adjusted_current_frame (event));
3847 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3852 framepos_t const pf = adjusted_current_frame (event);
3854 framepos_t grab = grab_frame ();
3855 _editor->snap_to_with_modifier (grab, event);
3857 /* base start and end on initial click position */
3869 _editor->zoom_rect->show();
3870 _editor->zoom_rect->raise_to_top();
3873 _editor->reposition_zoom_rect(start, end);
3875 show_verbose_cursor_time (pf);
3880 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3882 if (movement_occurred) {
3883 motion (event, false);
3885 if (grab_frame() < last_pointer_frame()) {
3886 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3888 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3891 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3892 _editor->tav_zoom_step (_zoom_out);
3894 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3898 _editor->zoom_rect->hide();
3902 MouseZoomDrag::aborted (bool)
3904 _editor->zoom_rect->hide ();
3907 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3909 , _cumulative_dx (0)
3910 , _cumulative_dy (0)
3912 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3914 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3915 _region = &_primary->region_view ();
3916 _note_height = _region->midi_stream_view()->note_height ();
3920 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3922 Drag::start_grab (event);
3924 if (!(_was_selected = _primary->selected())) {
3926 /* tertiary-click means extend selection - we'll do that on button release,
3927 so don't add it here, because otherwise we make it hard to figure
3928 out the "extend-to" range.
3931 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3934 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3937 _region->note_selected (_primary, true);
3939 _region->unique_select (_primary);
3945 /** @return Current total drag x change in frames */
3947 NoteDrag::total_dx () const
3950 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3952 /* primary note time */
3953 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3955 /* new time of the primary note in session frames */
3956 frameoffset_t st = n + dx;
3958 framepos_t const rp = _region->region()->position ();
3960 /* prevent the note being dragged earlier than the region's position */
3963 /* snap and return corresponding delta */
3964 return _region->snap_frame_to_frame (st - rp) + rp - n;
3967 /** @return Current total drag y change in note number */
3969 NoteDrag::total_dy () const
3971 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3975 NoteDrag::motion (GdkEvent *, bool)
3977 /* Total change in x and y since the start of the drag */
3978 frameoffset_t const dx = total_dx ();
3979 int8_t const dy = total_dy ();
3981 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3982 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3983 double const tdy = -dy * _note_height - _cumulative_dy;
3986 _cumulative_dx += tdx;
3987 _cumulative_dy += tdy;
3989 int8_t note_delta = total_dy();
3991 _region->move_selection (tdx, tdy, note_delta);
3993 /* the new note value may be the same as the old one, but we
3994 * don't know what that means because the selection may have
3995 * involved more than one note and we might be doing something
3996 * odd with them. so show the note value anyway, always.
4000 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4002 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4003 (int) floor (new_note));
4005 show_verbose_cursor_text (buf);
4010 NoteDrag::finished (GdkEvent* ev, bool moved)
4013 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4015 if (_was_selected) {
4016 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4018 _region->note_deselected (_primary);
4021 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4022 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4024 if (!extend && !add && _region->selection_size() > 1) {
4025 _region->unique_select (_primary);
4026 } else if (extend) {
4027 _region->note_selected (_primary, true, true);
4029 /* it was added during button press */
4034 _region->note_dropped (_primary, total_dx(), total_dy());
4039 NoteDrag::aborted (bool)
4044 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4045 : Drag (editor, item)
4047 , _nothing_to_drag (false)
4049 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4051 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4054 /* get all lines in the automation view */
4055 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4057 /* find those that overlap the ranges being dragged */
4058 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4059 while (i != lines.end ()) {
4060 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4063 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4065 /* check this range against all the AudioRanges that we are using */
4066 list<AudioRange>::const_iterator k = _ranges.begin ();
4067 while (k != _ranges.end()) {
4068 if (k->coverage (r.first, r.second) != OverlapNone) {
4074 /* add it to our list if it overlaps at all */
4075 if (k != _ranges.end()) {
4080 _lines.push_back (n);
4086 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4090 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4092 Drag::start_grab (event, cursor);
4094 /* Get line states before we start changing things */
4095 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4096 i->state = &i->line->get_state ();
4099 if (_ranges.empty()) {
4101 /* No selected time ranges: drag all points */
4102 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4103 uint32_t const N = i->line->npoints ();
4104 for (uint32_t j = 0; j < N; ++j) {
4105 i->points.push_back (i->line->nth (j));
4111 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4113 framecnt_t const half = (i->start + i->end) / 2;
4115 /* find the line that this audio range starts in */
4116 list<Line>::iterator j = _lines.begin();
4117 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4121 if (j != _lines.end()) {
4122 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4124 /* j is the line that this audio range starts in; fade into it;
4125 64 samples length plucked out of thin air.
4128 framepos_t a = i->start + 64;
4133 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4134 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4136 the_list->add (p, the_list->eval (p));
4137 j->line->add_always_in_view (p);
4138 the_list->add (q, the_list->eval (q));
4139 j->line->add_always_in_view (q);
4142 /* same thing for the end */
4145 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4149 if (j != _lines.end()) {
4150 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4152 /* j is the line that this audio range starts in; fade out of it;
4153 64 samples length plucked out of thin air.
4156 framepos_t b = i->end - 64;
4161 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4162 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4164 the_list->add (p, the_list->eval (p));
4165 j->line->add_always_in_view (p);
4166 the_list->add (q, the_list->eval (q));
4167 j->line->add_always_in_view (q);
4171 _nothing_to_drag = true;
4173 /* Find all the points that should be dragged and put them in the relevant
4174 points lists in the Line structs.
4177 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4179 uint32_t const N = i->line->npoints ();
4180 for (uint32_t j = 0; j < N; ++j) {
4182 /* here's a control point on this line */
4183 ControlPoint* p = i->line->nth (j);
4184 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4186 /* see if it's inside a range */
4187 list<AudioRange>::const_iterator k = _ranges.begin ();
4188 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4192 if (k != _ranges.end()) {
4193 /* dragging this point */
4194 _nothing_to_drag = false;
4195 i->points.push_back (p);
4201 if (_nothing_to_drag) {
4205 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4206 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4211 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4213 if (_nothing_to_drag) {
4217 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4218 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4220 /* we are ignoring x position for this drag, so we can just pass in anything */
4221 i->line->drag_motion (0, f, true, false);
4226 AutomationRangeDrag::finished (GdkEvent* event, bool)
4228 if (_nothing_to_drag) {
4232 motion (event, false);
4233 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4234 i->line->end_drag ();
4235 i->line->clear_always_in_view ();
4238 _editor->session()->commit_reversible_command ();
4242 AutomationRangeDrag::aborted (bool)
4244 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4245 i->line->clear_always_in_view ();
4250 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4253 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4254 layer = v->region()->layer ();
4255 initial_y = v->get_canvas_group()->property_y ();
4256 initial_playlist = v->region()->playlist ();
4257 initial_position = v->region()->position ();
4258 initial_end = v->region()->position () + v->region()->length ();
4261 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4265 , _cumulative_dx (0)
4267 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4271 PatchChangeDrag::motion (GdkEvent* ev, bool)
4273 framepos_t f = adjusted_current_frame (ev);
4274 boost::shared_ptr<Region> r = _region_view->region ();
4275 f = max (f, r->position ());
4276 f = min (f, r->last_frame ());
4278 framecnt_t const dxf = f - grab_frame();
4279 double const dxu = _editor->frame_to_unit (dxf);
4280 _patch_change->move (dxu - _cumulative_dx, 0);
4281 _cumulative_dx = dxu;
4285 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4287 if (!movement_occurred) {
4291 boost::shared_ptr<Region> r (_region_view->region ());
4293 framepos_t f = adjusted_current_frame (ev);
4294 f = max (f, r->position ());
4295 f = min (f, r->last_frame ());
4297 _region_view->move_patch_change (
4299 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4304 PatchChangeDrag::aborted (bool)
4306 _patch_change->move (-_cumulative_dx, 0);
4310 PatchChangeDrag::setup_pointer_frame_offset ()
4312 boost::shared_ptr<Region> region = _region_view->region ();
4313 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4316 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4317 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4324 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4326 framepos_t const p = _region_view->region()->position ();
4327 double const y = _region_view->midi_view()->y_position ();
4329 x1 = max ((framepos_t) 0, x1 - p);
4330 x2 = max ((framepos_t) 0, x2 - p);
4331 y1 = max (0.0, y1 - y);
4332 y2 = max (0.0, y2 - y);
4334 _region_view->update_drag_selection (
4335 _editor->frame_to_pixel (x1),
4336 _editor->frame_to_pixel (x2),
4339 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4344 MidiRubberbandSelectDrag::deselect_things ()
4349 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4350 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4353 _vertical_only = true;
4357 MidiVerticalSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4359 double const y = _region_view->midi_view()->y_position ();
4361 y1 = max (0.0, y1 - y);
4362 y2 = max (0.0, y2 - y);
4364 _region_view->update_vertical_drag_selection (
4367 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4372 MidiVerticalSelectDrag::deselect_things ()
4377 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4378 : RubberbandSelectDrag (e, i)
4384 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4386 if (drag_in_progress) {
4387 /* We just want to select things at the end of the drag, not during it */
4391 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4393 _editor->begin_reversible_command (_("rubberband selection"));
4394 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4395 _editor->commit_reversible_command ();
4399 EditorRubberbandSelectDrag::deselect_things ()
4401 if (!getenv("ARDOUR_SAE")) {
4402 _editor->selection->clear_tracks();
4404 _editor->selection->clear_regions();
4405 _editor->selection->clear_points ();
4406 _editor->selection->clear_lines ();
4409 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4417 NoteCreateDrag::~NoteCreateDrag ()
4423 NoteCreateDrag::grid_frames (framepos_t t) const
4426 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4431 return _region_view->region_beats_to_region_frames (grid_beats);
4435 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4437 Drag::start_grab (event, cursor);
4439 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4441 framepos_t pf = _drags->current_pointer_frame ();
4442 framecnt_t const g = grid_frames (pf);
4444 /* Hack so that we always snap to the note that we are over, instead of snapping
4445 to the next one if we're more than halfway through the one we're over.
4447 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4451 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4453 MidiStreamView* sv = _region_view->midi_stream_view ();
4454 double const x = _editor->frame_to_pixel (_note[0]);
4455 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4457 _drag_rect->property_x1() = x;
4458 _drag_rect->property_y1() = y;
4459 _drag_rect->property_x2() = x;
4460 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4462 _drag_rect->property_outline_what() = 0xff;
4463 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4464 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4468 NoteCreateDrag::motion (GdkEvent* event, bool)
4470 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4471 double const x = _editor->frame_to_pixel (_note[1]);
4472 if (_note[1] > _note[0]) {
4473 _drag_rect->property_x2() = x;
4475 _drag_rect->property_x1() = x;
4480 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4482 if (!had_movement) {
4486 framepos_t const start = min (_note[0], _note[1]);
4487 framecnt_t length = abs (_note[0] - _note[1]);
4489 framecnt_t const g = grid_frames (start);
4490 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4492 if (_editor->snap_mode() == SnapNormal && length < g) {
4493 length = g - one_tick;
4496 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4498 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4502 NoteCreateDrag::y_to_region (double y) const
4505 _region_view->get_canvas_group()->w2i (x, y);
4510 NoteCreateDrag::aborted (bool)