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 if (_editor->time_stretch (rs, percentage) == -1) {
3296 error << _("An error occurred while executing time stretch operation") << endmsg;
3301 TimeFXDrag::aborted (bool)
3303 _primary->get_time_axis_view().hide_timestretch ();
3306 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3309 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3313 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3315 Drag::start_grab (event);
3319 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3321 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3325 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3327 if (movement_occurred && _editor->session()) {
3328 /* make sure we stop */
3329 _editor->session()->request_transport_speed (0.0);
3334 ScrubDrag::aborted (bool)
3339 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3343 , _original_pointer_time_axis (-1)
3344 , _last_pointer_time_axis (-1)
3346 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3350 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3352 if (_editor->session() == 0) {
3356 Gdk::Cursor* cursor = 0;
3358 switch (_operation) {
3359 case CreateSelection:
3360 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3365 cursor = _editor->cursors()->selector;
3366 Drag::start_grab (event, cursor);
3369 case SelectionStartTrim:
3370 if (_editor->clicked_axisview) {
3371 _editor->clicked_axisview->order_selection_trims (_item, true);
3373 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3376 case SelectionEndTrim:
3377 if (_editor->clicked_axisview) {
3378 _editor->clicked_axisview->order_selection_trims (_item, false);
3380 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3384 Drag::start_grab (event, cursor);
3388 if (_operation == SelectionMove) {
3389 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3391 show_verbose_cursor_time (adjusted_current_frame (event));
3394 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3398 SelectionDrag::setup_pointer_frame_offset ()
3400 switch (_operation) {
3401 case CreateSelection:
3402 _pointer_frame_offset = 0;
3405 case SelectionStartTrim:
3407 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3410 case SelectionEndTrim:
3411 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3417 SelectionDrag::motion (GdkEvent* event, bool first_move)
3419 framepos_t start = 0;
3423 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3424 if (pending_time_axis.first == 0) {
3428 framepos_t const pending_position = adjusted_current_frame (event);
3430 /* only alter selection if things have changed */
3432 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3436 switch (_operation) {
3437 case CreateSelection:
3439 framepos_t grab = grab_frame ();
3442 _editor->snap_to (grab);
3445 if (pending_position < grab_frame()) {
3446 start = pending_position;
3449 end = pending_position;
3453 /* first drag: Either add to the selection
3454 or create a new selection
3460 /* adding to the selection */
3461 _editor->set_selected_track_as_side_effect (Selection::Add);
3462 //_editor->selection->add (_editor->clicked_axisview);
3463 _editor->clicked_selection = _editor->selection->add (start, end);
3468 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3469 //_editor->selection->set (_editor->clicked_axisview);
3470 _editor->set_selected_track_as_side_effect (Selection::Set);
3473 _editor->clicked_selection = _editor->selection->set (start, end);
3477 /* select the track that we're in */
3478 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3479 // _editor->set_selected_track_as_side_effect (Selection::Add);
3480 _editor->selection->add (pending_time_axis.first);
3481 _added_time_axes.push_back (pending_time_axis.first);
3484 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3485 tracks that we selected in the first place.
3488 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3489 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3491 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3492 while (i != _added_time_axes.end()) {
3494 list<TimeAxisView*>::iterator tmp = i;
3497 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3498 _editor->selection->remove (*i);
3499 _added_time_axes.remove (*i);
3508 case SelectionStartTrim:
3510 start = _editor->selection->time[_editor->clicked_selection].start;
3511 end = _editor->selection->time[_editor->clicked_selection].end;
3513 if (pending_position > end) {
3516 start = pending_position;
3520 case SelectionEndTrim:
3522 start = _editor->selection->time[_editor->clicked_selection].start;
3523 end = _editor->selection->time[_editor->clicked_selection].end;
3525 if (pending_position < start) {
3528 end = pending_position;
3535 start = _editor->selection->time[_editor->clicked_selection].start;
3536 end = _editor->selection->time[_editor->clicked_selection].end;
3538 length = end - start;
3540 start = pending_position;
3541 _editor->snap_to (start);
3543 end = start + length;
3548 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3549 _editor->start_canvas_autoscroll (1, 0);
3553 _editor->selection->replace (_editor->clicked_selection, start, end);
3556 if (_operation == SelectionMove) {
3557 show_verbose_cursor_time(start);
3559 show_verbose_cursor_time(pending_position);
3564 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3566 Session* s = _editor->session();
3568 if (movement_occurred) {
3569 motion (event, false);
3570 /* XXX this is not object-oriented programming at all. ick */
3571 if (_editor->selection->time.consolidate()) {
3572 _editor->selection->TimeChanged ();
3575 /* XXX what if its a music time selection? */
3576 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3577 s->request_play_range (&_editor->selection->time, true);
3582 /* just a click, no pointer movement.*/
3584 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3585 _editor->selection->clear_time();
3588 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3589 _editor->selection->set (_editor->clicked_axisview);
3592 if (s && s->get_play_range () && s->transport_rolling()) {
3593 s->request_stop (false, false);
3598 _editor->stop_canvas_autoscroll ();
3602 SelectionDrag::aborted (bool)
3607 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3612 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3614 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0,
3615 physical_screen_height (_editor->get_window()));
3616 _drag_rect->hide ();
3618 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3619 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3623 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3625 if (_editor->session() == 0) {
3629 Gdk::Cursor* cursor = 0;
3631 if (!_editor->temp_location) {
3632 _editor->temp_location = new Location (*_editor->session());
3635 switch (_operation) {
3636 case CreateRangeMarker:
3637 case CreateTransportMarker:
3638 case CreateCDMarker:
3640 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3645 cursor = _editor->cursors()->selector;
3649 Drag::start_grab (event, cursor);
3651 show_verbose_cursor_time (adjusted_current_frame (event));
3655 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3657 framepos_t start = 0;
3659 ArdourCanvas::SimpleRect *crect;
3661 switch (_operation) {
3662 case CreateRangeMarker:
3663 crect = _editor->range_bar_drag_rect;
3665 case CreateTransportMarker:
3666 crect = _editor->transport_bar_drag_rect;
3668 case CreateCDMarker:
3669 crect = _editor->cd_marker_bar_drag_rect;
3672 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3677 framepos_t const pf = adjusted_current_frame (event);
3679 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3680 framepos_t grab = grab_frame ();
3681 _editor->snap_to (grab);
3683 if (pf < grab_frame()) {
3691 /* first drag: Either add to the selection
3692 or create a new selection.
3697 _editor->temp_location->set (start, end);
3701 update_item (_editor->temp_location);
3703 //_drag_rect->raise_to_top();
3708 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3709 _editor->start_canvas_autoscroll (1, 0);
3713 _editor->temp_location->set (start, end);
3715 double x1 = _editor->frame_to_pixel (start);
3716 double x2 = _editor->frame_to_pixel (end);
3717 crect->property_x1() = x1;
3718 crect->property_x2() = x2;
3720 update_item (_editor->temp_location);
3723 show_verbose_cursor_time (pf);
3728 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3730 Location * newloc = 0;
3734 if (movement_occurred) {
3735 motion (event, false);
3738 switch (_operation) {
3739 case CreateRangeMarker:
3740 case CreateCDMarker:
3742 _editor->begin_reversible_command (_("new range marker"));
3743 XMLNode &before = _editor->session()->locations()->get_state();
3744 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3745 if (_operation == CreateCDMarker) {
3746 flags = Location::IsRangeMarker | Location::IsCDMarker;
3747 _editor->cd_marker_bar_drag_rect->hide();
3750 flags = Location::IsRangeMarker;
3751 _editor->range_bar_drag_rect->hide();
3753 newloc = new Location (
3754 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3757 _editor->session()->locations()->add (newloc, true);
3758 XMLNode &after = _editor->session()->locations()->get_state();
3759 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3760 _editor->commit_reversible_command ();
3764 case CreateTransportMarker:
3765 // popup menu to pick loop or punch
3766 _editor->new_transport_marker_context_menu (&event->button, _item);
3770 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3772 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3777 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3779 if (end == max_framepos) {
3780 end = _editor->session()->current_end_frame ();
3783 if (start == max_framepos) {
3784 start = _editor->session()->current_start_frame ();
3787 switch (_editor->mouse_mode) {
3789 /* find the two markers on either side and then make the selection from it */
3790 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3794 /* find the two markers on either side of the click and make the range out of it */
3795 _editor->selection->set (start, end);
3804 _editor->stop_canvas_autoscroll ();
3808 RangeMarkerBarDrag::aborted (bool)
3814 RangeMarkerBarDrag::update_item (Location* location)
3816 double const x1 = _editor->frame_to_pixel (location->start());
3817 double const x2 = _editor->frame_to_pixel (location->end());
3819 _drag_rect->property_x1() = x1;
3820 _drag_rect->property_x2() = x2;
3823 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3827 DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3831 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3833 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3834 Drag::start_grab (event, _editor->cursors()->zoom_out);
3837 Drag::start_grab (event, _editor->cursors()->zoom_in);
3841 show_verbose_cursor_time (adjusted_current_frame (event));
3845 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3850 framepos_t const pf = adjusted_current_frame (event);
3852 framepos_t grab = grab_frame ();
3853 _editor->snap_to_with_modifier (grab, event);
3855 /* base start and end on initial click position */
3867 _editor->zoom_rect->show();
3868 _editor->zoom_rect->raise_to_top();
3871 _editor->reposition_zoom_rect(start, end);
3873 show_verbose_cursor_time (pf);
3878 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3880 if (movement_occurred) {
3881 motion (event, false);
3883 if (grab_frame() < last_pointer_frame()) {
3884 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3886 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3889 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
3890 _editor->tav_zoom_step (_zoom_out);
3892 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3896 _editor->zoom_rect->hide();
3900 MouseZoomDrag::aborted (bool)
3902 _editor->zoom_rect->hide ();
3905 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3907 , _cumulative_dx (0)
3908 , _cumulative_dy (0)
3910 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3912 _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3913 _region = &_primary->region_view ();
3914 _note_height = _region->midi_stream_view()->note_height ();
3918 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3920 Drag::start_grab (event);
3922 if (!(_was_selected = _primary->selected())) {
3924 /* tertiary-click means extend selection - we'll do that on button release,
3925 so don't add it here, because otherwise we make it hard to figure
3926 out the "extend-to" range.
3929 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3932 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3935 _region->note_selected (_primary, true);
3937 _region->unique_select (_primary);
3943 /** @return Current total drag x change in frames */
3945 NoteDrag::total_dx () const
3948 frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3950 /* primary note time */
3951 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
3953 /* new time of the primary note in session frames */
3954 frameoffset_t st = n + dx;
3956 framepos_t const rp = _region->region()->position ();
3958 /* prevent the note being dragged earlier than the region's position */
3961 /* snap and return corresponding delta */
3962 return _region->snap_frame_to_frame (st - rp) + rp - n;
3965 /** @return Current total drag y change in note number */
3967 NoteDrag::total_dy () const
3969 return ((int8_t) (grab_y() / _note_height)) - ((int8_t) (_drags->current_pointer_y() / _note_height));
3973 NoteDrag::motion (GdkEvent *, bool)
3975 /* Total change in x and y since the start of the drag */
3976 frameoffset_t const dx = total_dx ();
3977 int8_t const dy = total_dy ();
3979 /* Now work out what we have to do to the note canvas items to set this new drag delta */
3980 double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3981 double const tdy = -dy * _note_height - _cumulative_dy;
3984 _cumulative_dx += tdx;
3985 _cumulative_dy += tdy;
3987 int8_t note_delta = total_dy();
3989 _region->move_selection (tdx, tdy, note_delta);
3991 /* the new note value may be the same as the old one, but we
3992 * don't know what that means because the selection may have
3993 * involved more than one note and we might be doing something
3994 * odd with them. so show the note value anyway, always.
3998 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4000 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4001 (int) floor (new_note));
4003 show_verbose_cursor_text (buf);
4008 NoteDrag::finished (GdkEvent* ev, bool moved)
4011 if (_editor->current_mouse_mode() == Editing::MouseObject) {
4013 if (_was_selected) {
4014 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4016 _region->note_deselected (_primary);
4019 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4020 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4022 if (!extend && !add && _region->selection_size() > 1) {
4023 _region->unique_select (_primary);
4024 } else if (extend) {
4025 _region->note_selected (_primary, true, true);
4027 /* it was added during button press */
4032 _region->note_dropped (_primary, total_dx(), total_dy());
4037 NoteDrag::aborted (bool)
4042 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
4043 : Drag (editor, item)
4045 , _nothing_to_drag (false)
4047 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4049 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
4052 /* get all lines in the automation view */
4053 list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
4055 /* find those that overlap the ranges being dragged */
4056 list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
4057 while (i != lines.end ()) {
4058 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
4061 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
4063 /* check this range against all the AudioRanges that we are using */
4064 list<AudioRange>::const_iterator k = _ranges.begin ();
4065 while (k != _ranges.end()) {
4066 if (k->coverage (r.first, r.second) != OverlapNone) {
4072 /* add it to our list if it overlaps at all */
4073 if (k != _ranges.end()) {
4078 _lines.push_back (n);
4084 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4088 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4090 Drag::start_grab (event, cursor);
4092 /* Get line states before we start changing things */
4093 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4094 i->state = &i->line->get_state ();
4097 if (_ranges.empty()) {
4099 /* No selected time ranges: drag all points */
4100 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4101 uint32_t const N = i->line->npoints ();
4102 for (uint32_t j = 0; j < N; ++j) {
4103 i->points.push_back (i->line->nth (j));
4109 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4111 framecnt_t const half = (i->start + i->end) / 2;
4113 /* find the line that this audio range starts in */
4114 list<Line>::iterator j = _lines.begin();
4115 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4119 if (j != _lines.end()) {
4120 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4122 /* j is the line that this audio range starts in; fade into it;
4123 64 samples length plucked out of thin air.
4126 framepos_t a = i->start + 64;
4131 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4132 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4134 the_list->add (p, the_list->eval (p));
4135 j->line->add_always_in_view (p);
4136 the_list->add (q, the_list->eval (q));
4137 j->line->add_always_in_view (q);
4140 /* same thing for the end */
4143 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4147 if (j != _lines.end()) {
4148 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4150 /* j is the line that this audio range starts in; fade out of it;
4151 64 samples length plucked out of thin air.
4154 framepos_t b = i->end - 64;
4159 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4160 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4162 the_list->add (p, the_list->eval (p));
4163 j->line->add_always_in_view (p);
4164 the_list->add (q, the_list->eval (q));
4165 j->line->add_always_in_view (q);
4169 _nothing_to_drag = true;
4171 /* Find all the points that should be dragged and put them in the relevant
4172 points lists in the Line structs.
4175 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4177 uint32_t const N = i->line->npoints ();
4178 for (uint32_t j = 0; j < N; ++j) {
4180 /* here's a control point on this line */
4181 ControlPoint* p = i->line->nth (j);
4182 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4184 /* see if it's inside a range */
4185 list<AudioRange>::const_iterator k = _ranges.begin ();
4186 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4190 if (k != _ranges.end()) {
4191 /* dragging this point */
4192 _nothing_to_drag = false;
4193 i->points.push_back (p);
4199 if (_nothing_to_drag) {
4203 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4204 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4209 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4211 if (_nothing_to_drag) {
4215 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4216 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4218 /* we are ignoring x position for this drag, so we can just pass in anything */
4219 i->line->drag_motion (0, f, true, false);
4224 AutomationRangeDrag::finished (GdkEvent* event, bool)
4226 if (_nothing_to_drag) {
4230 motion (event, false);
4231 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4232 i->line->end_drag ();
4233 i->line->clear_always_in_view ();
4236 _editor->session()->commit_reversible_command ();
4240 AutomationRangeDrag::aborted (bool)
4242 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4243 i->line->clear_always_in_view ();
4248 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4251 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4252 layer = v->region()->layer ();
4253 initial_y = v->get_canvas_group()->property_y ();
4254 initial_playlist = v->region()->playlist ();
4255 initial_position = v->region()->position ();
4256 initial_end = v->region()->position () + v->region()->length ();
4259 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4263 , _cumulative_dx (0)
4265 DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4269 PatchChangeDrag::motion (GdkEvent* ev, bool)
4271 framepos_t f = adjusted_current_frame (ev);
4272 boost::shared_ptr<Region> r = _region_view->region ();
4273 f = max (f, r->position ());
4274 f = min (f, r->last_frame ());
4276 framecnt_t const dxf = f - grab_frame();
4277 double const dxu = _editor->frame_to_unit (dxf);
4278 _patch_change->move (dxu - _cumulative_dx, 0);
4279 _cumulative_dx = dxu;
4283 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4285 if (!movement_occurred) {
4289 boost::shared_ptr<Region> r (_region_view->region ());
4291 framepos_t f = adjusted_current_frame (ev);
4292 f = max (f, r->position ());
4293 f = min (f, r->last_frame ());
4295 _region_view->move_patch_change (
4297 _region_view->region_frames_to_region_beats (f - r->position() - r->start())
4302 PatchChangeDrag::aborted (bool)
4304 _patch_change->move (-_cumulative_dx, 0);
4308 PatchChangeDrag::setup_pointer_frame_offset ()
4310 boost::shared_ptr<Region> region = _region_view->region ();
4311 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4314 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4315 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4322 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4324 framepos_t const p = _region_view->region()->position ();
4325 double const y = _region_view->midi_view()->y_position ();
4327 x1 = max ((framepos_t) 0, x1 - p);
4328 x2 = max ((framepos_t) 0, x2 - p);
4329 y1 = max (0.0, y1 - y);
4330 y2 = max (0.0, y2 - y);
4332 _region_view->update_drag_selection (
4333 _editor->frame_to_pixel (x1),
4334 _editor->frame_to_pixel (x2),
4337 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4342 MidiRubberbandSelectDrag::deselect_things ()
4347 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4348 : RubberbandSelectDrag (e, rv->get_canvas_frame ())
4351 _vertical_only = true;
4355 MidiVerticalSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4357 double const y = _region_view->midi_view()->y_position ();
4359 y1 = max (0.0, y1 - y);
4360 y2 = max (0.0, y2 - y);
4362 _region_view->update_vertical_drag_selection (
4365 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4370 MidiVerticalSelectDrag::deselect_things ()
4375 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4376 : RubberbandSelectDrag (e, i)
4382 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4384 if (drag_in_progress) {
4385 /* We just want to select things at the end of the drag, not during it */
4389 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4391 _editor->begin_reversible_command (_("rubberband selection"));
4392 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4393 _editor->commit_reversible_command ();
4397 EditorRubberbandSelectDrag::deselect_things ()
4399 if (!getenv("ARDOUR_SAE")) {
4400 _editor->selection->clear_tracks();
4402 _editor->selection->clear_regions();
4403 _editor->selection->clear_points ();
4404 _editor->selection->clear_lines ();
4407 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4415 NoteCreateDrag::~NoteCreateDrag ()
4421 NoteCreateDrag::grid_frames (framepos_t t) const
4424 Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4429 return _region_view->region_beats_to_region_frames (grid_beats);
4433 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4435 Drag::start_grab (event, cursor);
4437 _drag_rect = new ArdourCanvas::SimpleRect (*_region_view->get_canvas_group ());
4439 framepos_t pf = _drags->current_pointer_frame ();
4440 framecnt_t const g = grid_frames (pf);
4442 /* Hack so that we always snap to the note that we are over, instead of snapping
4443 to the next one if we're more than halfway through the one we're over.
4445 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4449 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4451 MidiStreamView* sv = _region_view->midi_stream_view ();
4452 double const x = _editor->frame_to_pixel (_note[0]);
4453 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4455 _drag_rect->property_x1() = x;
4456 _drag_rect->property_y1() = y;
4457 _drag_rect->property_x2() = x;
4458 _drag_rect->property_y2() = y + floor (_region_view->midi_stream_view()->note_height ());
4460 _drag_rect->property_outline_what() = 0xff;
4461 _drag_rect->property_outline_color_rgba() = 0xffffff99;
4462 _drag_rect->property_fill_color_rgba() = 0xffffff66;
4466 NoteCreateDrag::motion (GdkEvent* event, bool)
4468 _note[1] = adjusted_current_frame (event) - _region_view->region()->position ();
4469 double const x = _editor->frame_to_pixel (_note[1]);
4470 if (_note[1] > _note[0]) {
4471 _drag_rect->property_x2() = x;
4473 _drag_rect->property_x1() = x;
4478 NoteCreateDrag::finished (GdkEvent* event, bool had_movement)
4480 if (!had_movement) {
4484 framepos_t const start = min (_note[0], _note[1]);
4485 framecnt_t length = abs (_note[0] - _note[1]);
4487 framecnt_t const g = grid_frames (start);
4488 double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4490 if (_editor->snap_mode() == SnapNormal && length < g) {
4491 length = g - one_tick;
4494 double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4496 _region_view->create_note_at (start, _drag_rect->property_y1(), length_beats, false);
4500 NoteCreateDrag::y_to_region (double y) const
4503 _region_view->get_canvas_group()->w2i (x, y);
4508 NoteCreateDrag::aborted (bool)