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.
20 #include "pbd/memento_command.h"
21 #include "pbd/basename.h"
22 #include "ardour/diskstream.h"
23 #include "ardour/dB.h"
24 #include "ardour/region_factory.h"
25 #include "ardour/midi_diskstream.h"
29 #include "audio_region_view.h"
30 #include "midi_region_view.h"
31 #include "ardour_ui.h"
32 #include "control_point.h"
34 #include "region_gain_line.h"
35 #include "editor_drag.h"
36 #include "audio_time_axis.h"
37 #include "midi_time_axis.h"
38 #include "canvas-note.h"
39 #include "selection.h"
40 #include "midi_selection.h"
43 using namespace ARDOUR;
47 using namespace Editing;
48 using namespace ArdourCanvas;
50 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
52 Drag::Drag (Editor* e, ArdourCanvas::Item* i) :
55 _pointer_frame_offset (0),
57 _last_pointer_frame (0),
58 _current_pointer_frame (0),
59 _had_movement (false),
60 _move_threshold_passed (false)
66 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
72 cursor = _editor->which_grabber_cursor ();
75 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
79 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
82 cursor = _editor->which_grabber_cursor ();
85 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
87 if (Keyboard::is_button2_event (&event->button)) {
88 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
89 _y_constrained = true;
90 _x_constrained = false;
92 _y_constrained = false;
93 _x_constrained = true;
96 _x_constrained = false;
97 _y_constrained = false;
100 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
101 _last_pointer_frame = _grab_frame;
102 _current_pointer_frame = _grab_frame;
103 _current_pointer_x = _grab_x;
104 _current_pointer_y = _grab_y;
105 _last_pointer_x = _current_pointer_x;
106 _last_pointer_y = _current_pointer_y;
110 _item->i2w (_original_x, _original_y);
112 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
116 if (_editor->session && _editor->session->transport_rolling()) {
119 _was_rolling = false;
122 switch (_editor->snap_type()) {
123 case SnapToRegionStart:
124 case SnapToRegionEnd:
125 case SnapToRegionSync:
126 case SnapToRegionBoundary:
127 _editor->build_region_boundary_cache ();
134 /** @param event GDK event, or 0.
135 * @return true if some movement occurred, otherwise false.
138 Drag::end_grab (GdkEvent* event)
142 _editor->stop_canvas_autoscroll ();
144 _item->ungrab (event ? event->button.time : 0);
146 _last_pointer_x = _current_pointer_x;
147 _last_pointer_y = _current_pointer_y;
148 finished (event, _had_movement);
150 _editor->hide_verbose_canvas_cursor();
154 return _had_movement;
158 Drag::adjusted_current_frame (GdkEvent* event) const
162 if (_current_pointer_frame > _pointer_frame_offset) {
163 pos = _current_pointer_frame - _pointer_frame_offset;
166 _editor->snap_to_with_modifier (pos, event);
172 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
174 _last_pointer_x = _current_pointer_x;
175 _last_pointer_y = _current_pointer_y;
176 _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
178 if (!from_autoscroll && !_move_threshold_passed) {
180 bool const xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
181 bool const yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
183 _move_threshold_passed = (xp || yp);
185 if (apply_move_threshold() && _move_threshold_passed) {
187 _grab_frame = _current_pointer_frame;
188 _grab_x = _current_pointer_x;
189 _grab_y = _current_pointer_y;
190 _last_pointer_frame = _grab_frame;
191 _pointer_frame_offset = _grab_frame - _last_frame_position;
196 bool old_had_movement = _had_movement;
198 /* a motion event has happened, so we've had movement... */
199 _had_movement = true;
201 /* ... unless we're using a move threshold and we've not yet passed it */
202 if (apply_move_threshold() && !_move_threshold_passed) {
203 _had_movement = false;
206 if (active (_editor->mouse_mode)) {
208 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
209 if (!from_autoscroll) {
210 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
213 motion (event, _had_movement != old_had_movement);
225 _editor->stop_canvas_autoscroll ();
226 _editor->hide_verbose_canvas_cursor ();
231 /* put it back where it came from */
236 _item->i2w (cxw, cyw);
237 _item->move (_original_x - cxw, _original_y - cyw);
242 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
247 RegionView::RegionViewGoingAway.connect (mem_fun (*this, &RegionDrag::region_going_away));
251 RegionDrag::region_going_away (RegionView* v)
256 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
257 : RegionDrag (e, i, p, v),
267 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
269 Drag::start_grab (event);
271 _editor->show_verbose_time_cursor (_last_frame_position, 10);
274 RegionMotionDrag::TimeAxisViewSummary
275 RegionMotionDrag::get_time_axis_view_summary ()
277 int32_t children = 0;
278 TimeAxisViewSummary sum;
280 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
282 /* get a bitmask representing the visible tracks */
284 for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
285 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
286 TimeAxisView::Children children_list;
288 /* zeroes are audio/MIDI tracks. ones are other types. */
290 if (!rtv->hidden()) {
292 if (!rtv->is_track()) {
293 /* not an audio nor MIDI track */
294 sum.tracks = sum.tracks |= (0x01 << rtv->order());
297 sum.height_list[rtv->order()] = (*i)->current_height();
300 if ((children_list = rtv->get_child_list()).size() > 0) {
301 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
302 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
303 sum.height_list[rtv->order() + children] = (*j)->current_height();
314 RegionMotionDrag::compute_y_delta (
315 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
316 int32_t last_pointer_layer, int32_t current_pointer_layer,
317 TimeAxisViewSummary const & tavs,
318 int32_t* pointer_order_span, int32_t* pointer_layer_span,
319 int32_t* canvas_pointer_order_span
323 *pointer_order_span = 0;
324 *pointer_layer_span = 0;
328 bool clamp_y_axis = false;
330 /* the change in track order between this callback and the last */
331 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
332 /* the change in layer between this callback and the last;
333 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
334 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
336 if (*pointer_order_span != 0) {
338 /* find the actual pointer span, in terms of the number of visible tracks;
339 to do this, we reduce |pointer_order_span| by the number of hidden tracks
342 *canvas_pointer_order_span = *pointer_order_span;
343 if (last_pointer_view->order() >= current_pointer_view->order()) {
344 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
345 if (tavs.height_list[y] == 0) {
346 *canvas_pointer_order_span--;
350 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
351 if (tavs.height_list[y] == 0) {
352 *canvas_pointer_order_span++;
357 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
359 RegionView* rv = (*i);
361 if (rv->region()->locked()) {
365 double ix1, ix2, iy1, iy2;
366 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
367 rv->get_canvas_frame()->i2w (ix1, iy1);
368 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
370 /* get the new trackview for this particular region */
371 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
373 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
375 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
376 as surely this is a per-region thing... */
378 clamp_y_axis = y_movement_disallowed (
379 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
387 } else if (_dest_trackview == current_pointer_view) {
389 if (current_pointer_layer == last_pointer_layer) {
390 /* No movement; clamp */
396 _dest_trackview = current_pointer_view;
397 _dest_layer = current_pointer_layer;
405 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
407 *pending_region_position = 0;
409 /* compute the amount of pointer motion in frames, and where
410 the region would be if we moved it by that much.
412 if (_current_pointer_frame >= _pointer_frame_offset) {
414 nframes64_t sync_frame;
415 nframes64_t sync_offset;
418 *pending_region_position = _current_pointer_frame - _pointer_frame_offset;
420 sync_offset = _primary->region()->sync_offset (sync_dir);
422 /* we don't handle a sync point that lies before zero.
424 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
426 sync_frame = *pending_region_position + (sync_dir*sync_offset);
428 _editor->snap_to_with_modifier (sync_frame, event);
430 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
433 *pending_region_position = _last_frame_position;
438 if (*pending_region_position > max_frames - _primary->region()->length()) {
439 *pending_region_position = _last_frame_position;
444 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
446 /* now compute the canvas unit distance we need to move the regionview
447 to make it appear at the new location.
450 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
452 if (*pending_region_position <= _last_frame_position) {
454 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
456 RegionView* rv = (*i);
458 // If any regionview is at zero, we need to know so we can stop further leftward motion.
460 double ix1, ix2, iy1, iy2;
461 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
462 rv->get_canvas_frame()->i2w (ix1, iy1);
464 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
466 *pending_region_position = _last_frame_position;
473 _last_frame_position = *pending_region_position;
480 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
484 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
486 vector<int32_t>::iterator j;
488 /* *pointer* variables reflect things about the pointer; as we may be moving
489 multiple regions, much detail must be computed per-region */
491 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
492 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
493 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
494 is always 0 regardless of what the region's "real" layer is */
495 RouteTimeAxisView* current_pointer_view;
496 layer_t current_pointer_layer;
497 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
501 /* TimeAxisView that we were pointing at last time we entered this method */
502 TimeAxisView const * const last_pointer_view = _dest_trackview;
503 /* the order of the track that we were pointing at last time we entered this method */
504 int32_t const last_pointer_order = last_pointer_view->order ();
505 /* the layer that we were pointing at last time we entered this method */
506 layer_t const last_pointer_layer = _dest_layer;
508 int32_t pointer_order_span;
509 int32_t pointer_layer_span;
510 int32_t canvas_pointer_order_span;
512 bool const clamp_y_axis = compute_y_delta (
513 last_pointer_view, current_pointer_view,
514 last_pointer_layer, current_pointer_layer, tavs,
515 &pointer_order_span, &pointer_layer_span,
516 &canvas_pointer_order_span
519 nframes64_t pending_region_position;
520 double const x_delta = compute_x_delta (event, &pending_region_position);
522 /*************************************************************
524 ************************************************************/
526 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
527 /* haven't reached next snap point, and we're not switching
528 trackviews nor layers. nothing to do.
533 /*************************************************************
535 ************************************************************/
537 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
539 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
541 RegionView* rv = (*i);
543 if (rv->region()->locked()) {
547 /* here we are calculating the y distance from the
548 top of the first track view to the top of the region
549 area of the track view that we're working on */
551 /* this x value is just a dummy value so that we have something
556 /* distance from the top of this track view to the region area
557 of our track view is always 1 */
561 /* convert to world coordinates, ie distance from the top of
564 rv->get_canvas_frame()->i2w (ix1, iy1);
566 /* compensate for the ruler section and the vertical scrollbar position */
567 iy1 += _editor->get_trackview_group_vertical_offset ();
571 // hide any dependent views
573 rv->get_time_axis_view().hide_dependent_views (*rv);
576 reparent to a non scrolling group so that we can keep the
577 region selection above all time axis views.
578 reparenting means we have to move the rv as the two
579 parent groups have different coordinates.
582 rv->get_canvas_group()->property_y() = iy1 - 1;
583 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
585 rv->fake_set_opaque (true);
588 /* current view for this particular region */
589 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
590 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
592 if (pointer_order_span != 0 && !clamp_y_axis) {
594 /* INTER-TRACK MOVEMENT */
596 /* move through the height list to the track that the region is currently on */
597 vector<int32_t>::iterator j = tavs.height_list.begin ();
599 while (j != tavs.height_list.end () && x != rtv->order ()) {
605 int32_t temp_pointer_order_span = canvas_pointer_order_span;
607 if (j != tavs.height_list.end ()) {
609 /* Account for layers in the original and
610 destination tracks. If we're moving around in layers we assume
611 that only one track is involved, so it's ok to use *pointer*
614 StreamView* lv = last_pointer_view->view ();
617 /* move to the top of the last trackview */
618 if (lv->layer_display () == Stacked) {
619 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
622 StreamView* cv = current_pointer_view->view ();
625 /* move to the right layer on the current trackview */
626 if (cv->layer_display () == Stacked) {
627 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
630 /* And for being on a non-topmost layer on the new
633 while (temp_pointer_order_span > 0) {
634 /* we're moving up canvas-wise,
635 so we need to find the next track height
637 if (j != tavs.height_list.begin()) {
641 if (x != last_pointer_order) {
643 ++temp_pointer_order_span;
648 temp_pointer_order_span--;
651 while (temp_pointer_order_span < 0) {
655 if (x != last_pointer_order) {
657 --temp_pointer_order_span;
661 if (j != tavs.height_list.end()) {
665 temp_pointer_order_span++;
669 /* find out where we'll be when we move and set height accordingly */
671 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
672 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
673 rv->set_height (temp_rtv->view()->child_height());
675 /* if you un-comment the following, the region colours will follow
676 the track colours whilst dragging; personally
677 i think this can confuse things, but never mind.
680 //const GdkColor& col (temp_rtv->view->get_region_color());
681 //rv->set_color (const_cast<GdkColor&>(col));
685 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
687 /* INTER-LAYER MOVEMENT in the same track */
688 y_delta = rtv->view()->child_height () * pointer_layer_span;
693 _editor->mouse_brush_insert_region (rv, pending_region_position);
695 rv->move (x_delta, y_delta);
698 } /* foreach region */
701 _editor->cursor_group->raise_to_top();
704 if (x_delta != 0 && !_brushing) {
705 _editor->show_verbose_time_cursor (_last_frame_position, 10);
710 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
712 if (_copy && first_move) {
713 copy_regions (event);
716 RegionMotionDrag::motion (event, first_move);
720 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
722 vector<RegionView*> copies;
723 boost::shared_ptr<Diskstream> ds;
724 boost::shared_ptr<Playlist> from_playlist;
725 RegionSelection new_views;
726 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
727 PlaylistSet modified_playlists;
728 PlaylistSet frozen_playlists;
729 list <sigc::connection> modified_playlist_connections;
730 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
731 nframes64_t drag_delta;
732 bool changed_tracks, changed_position;
733 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
734 RouteTimeAxisView* source_tv;
736 if (!movement_occurred) {
741 if (Config->get_edit_mode() == Splice && !_editor->pre_drag_region_selection.empty()) {
742 _editor->selection->set (_editor->pre_drag_region_selection);
743 _editor->pre_drag_region_selection.clear ();
747 /* all changes were made during motion event handlers */
750 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
751 copies.push_back (*i);
758 /* reverse this here so that we have the correct logic to finalize
762 if (Config->get_edit_mode() == Lock) {
763 _x_constrained = !_x_constrained;
767 if (_x_constrained) {
768 _editor->begin_reversible_command (_("fixed time region copy"));
770 _editor->begin_reversible_command (_("region copy"));
773 if (_x_constrained) {
774 _editor->begin_reversible_command (_("fixed time region drag"));
776 _editor->begin_reversible_command (_("region drag"));
780 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
781 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
783 drag_delta = _primary->region()->position() - _last_frame_position;
785 _editor->update_canvas_now ();
787 /* make a list of where each region ended up */
788 final = find_time_axis_views_and_layers ();
790 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
792 RegionView* rv = (*i);
793 RouteTimeAxisView* dest_rtv = final[*i].first;
794 layer_t dest_layer = final[*i].second;
798 if (rv->region()->locked()) {
803 if (changed_position && !_x_constrained) {
804 where = rv->region()->position() - drag_delta;
806 where = rv->region()->position();
809 boost::shared_ptr<Region> new_region;
812 /* we already made a copy */
813 new_region = rv->region();
815 /* undo the previous hide_dependent_views so that xfades don't
816 disappear on copying regions
819 //rv->get_time_axis_view().reveal_dependent_views (*rv);
821 } else if (changed_tracks && dest_rtv->playlist()) {
822 new_region = RegionFactory::create (rv->region());
825 if (changed_tracks || _copy) {
827 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
834 _editor->latest_regionviews.clear ();
836 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*_editor, &Editor::collect_new_region_view));
838 insert_result = modified_playlists.insert (to_playlist);
840 if (insert_result.second) {
841 _editor->session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
844 to_playlist->add_region (new_region, where);
845 if (dest_rtv->view()->layer_display() == Stacked) {
846 new_region->set_layer (dest_layer);
847 new_region->set_pending_explicit_relayer (true);
852 if (!_editor->latest_regionviews.empty()) {
853 // XXX why just the first one ? we only expect one
854 // commented out in nick_m's canvas reworking. is that intended?
855 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
856 new_views.push_back (_editor->latest_regionviews.front());
861 motion on the same track. plonk the previously reparented region
862 back to its original canvas group (its streamview).
863 No need to do anything for copies as they are fake regions which will be deleted.
866 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
867 rv->get_canvas_group()->property_y() = 0;
869 /* just change the model */
871 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
873 if (dest_rtv->view()->layer_display() == Stacked) {
874 rv->region()->set_layer (dest_layer);
875 rv->region()->set_pending_explicit_relayer (true);
878 insert_result = modified_playlists.insert (playlist);
880 if (insert_result.second) {
881 _editor->session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
883 /* freeze to avoid lots of relayering in the case of a multi-region drag */
884 frozen_insert_result = frozen_playlists.insert(playlist);
886 if (frozen_insert_result.second) {
890 rv->region()->set_position (where, (void*) this);
893 if (changed_tracks && !_copy) {
895 /* get the playlist where this drag started. we can't use rv->region()->playlist()
896 because we may have copied the region and it has not been attached to a playlist.
899 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
900 ds = source_tv->get_diskstream();
901 from_playlist = ds->playlist();
905 assert (from_playlist);
907 /* moved to a different audio track, without copying */
909 /* the region that used to be in the old playlist is not
910 moved to the new one - we use a copy of it. as a result,
911 any existing editor for the region should no longer be
915 rv->hide_region_editor();
916 rv->fake_set_opaque (false);
918 /* remove the region from the old playlist */
920 insert_result = modified_playlists.insert (from_playlist);
922 if (insert_result.second) {
923 _editor->session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
926 from_playlist->remove_region (rv->region());
928 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
929 was selected in all of them, then removing it from a playlist will have removed all
930 trace of it from the selection (i.e. there were N regions selected, we removed 1,
931 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
932 corresponding regionview, and the selection is now empty).
934 this could have invalidated any and all iterators into the region selection.
936 the heuristic we use here is: if the region selection is empty, break out of the loop
937 here. if the region selection is not empty, then restart the loop because we know that
938 we must have removed at least the region(view) we've just been working on as well as any
939 that we processed on previous iterations.
941 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
945 if (_views.empty()) {
956 copies.push_back (rv);
960 _editor->selection->add (new_views);
962 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
967 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
968 _editor->session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
971 _editor->commit_reversible_command ();
973 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
980 RegionMotionDrag::x_move_allowed () const
982 if (Config->get_edit_mode() == Lock) {
983 /* in locked edit mode, reverse the usual meaning of _x_constrained */
984 return _x_constrained;
987 return !_x_constrained;
991 RegionMotionDrag::copy_regions (GdkEvent* event)
993 /* duplicate the regionview(s) and region(s) */
995 list<RegionView*> new_regionviews;
997 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
999 RegionView* rv = (*i);
1000 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1001 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1003 const boost::shared_ptr<const Region> original = rv->region();
1004 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1008 boost::shared_ptr<AudioRegion> audioregion_copy
1009 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1010 nrv = new AudioRegionView (*arv, audioregion_copy);
1012 boost::shared_ptr<MidiRegion> midiregion_copy
1013 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1014 nrv = new MidiRegionView (*mrv, midiregion_copy);
1019 nrv->get_canvas_group()->show ();
1020 new_regionviews.push_back (nrv);
1023 if (new_regionviews.empty()) {
1027 /* reflect the fact that we are dragging the copies */
1029 _primary = new_regionviews.front();
1030 _views = new_regionviews;
1032 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1035 sync the canvas to what we think is its current state
1036 without it, the canvas seems to
1037 "forget" to update properly after the upcoming reparent()
1038 ..only if the mouse is in rapid motion at the time of the grab.
1039 something to do with regionview creation raking so long?
1041 _editor->update_canvas_now();
1045 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1047 /* Which trackview is this ? */
1049 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1050 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1051 (*layer) = tvp.second;
1053 if (*tv && (*tv)->layer_display() == Overlaid) {
1057 /* The region motion is only processed if the pointer is over
1061 if (!(*tv) || !(*tv)->is_track()) {
1062 /* To make sure we hide the verbose canvas cursor when the mouse is
1063 not held over and audiotrack.
1065 _editor->hide_verbose_canvas_cursor ();
1072 /** @param new_order New track order.
1073 * @param old_order Old track order.
1074 * @param visible_y_low Lowest visible order.
1075 * @return true if y movement should not happen, otherwise false.
1078 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1080 if (new_order != old_order) {
1082 /* this isn't the pointer track */
1086 /* moving up the canvas */
1087 if ( (new_order - y_span) >= tavs.visible_y_low) {
1091 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1092 int32_t visible_tracks = 0;
1093 while (visible_tracks < y_span ) {
1095 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1096 /* passing through a hidden track */
1101 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1102 /* moving to a non-track; disallow */
1108 /* moving beyond the lowest visible track; disallow */
1112 } else if (y_span < 0) {
1114 /* moving down the canvas */
1115 if ((new_order - y_span) <= tavs.visible_y_high) {
1117 int32_t visible_tracks = 0;
1119 while (visible_tracks > y_span ) {
1122 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1123 /* passing through a hidden track */
1128 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1129 /* moving to a non-track; disallow */
1136 /* moving beyond the highest visible track; disallow */
1143 /* this is the pointer's track */
1145 if ((new_order - y_span) > tavs.visible_y_high) {
1146 /* we will overflow */
1148 } else if ((new_order - y_span) < tavs.visible_y_low) {
1149 /* we will overflow */
1158 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1159 : RegionMotionDrag (e, i, p, v, b),
1162 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1164 _dest_trackview = tv;
1165 if (tv->layer_display() == Overlaid) {
1168 _dest_layer = _primary->region()->layer ();
1172 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1173 if (rtv && rtv->is_track()) {
1174 speed = rtv->get_diskstream()->speed ();
1177 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1181 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1183 RegionMotionDrag::start_grab (event, c);
1185 _pointer_frame_offset = _grab_frame - _last_frame_position;
1188 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1189 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1191 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1192 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1194 _primary = v->view()->create_region_view (r, false, false);
1196 _primary->get_canvas_group()->show ();
1197 _primary->set_position (pos, 0);
1198 _views.push_back (_primary);
1200 _last_frame_position = pos;
1202 _item = _primary->get_canvas_group ();
1203 _dest_trackview = v;
1204 _dest_layer = _primary->region()->layer ();
1207 map<RegionView*, pair<RouteTimeAxisView*, int> >
1208 RegionMotionDrag::find_time_axis_views_and_layers ()
1210 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1212 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1214 double ix1, ix2, iy1, iy2;
1215 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1216 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1217 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1219 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1220 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1228 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1230 _editor->update_canvas_now ();
1232 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1234 RouteTimeAxisView* dest_rtv = final[_primary].first;
1236 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1237 _primary->get_canvas_group()->property_y() = 0;
1239 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1241 _editor->begin_reversible_command (_("insert region"));
1242 XMLNode& before = playlist->get_state ();
1243 playlist->add_region (_primary->region (), _last_frame_position);
1244 _editor->session->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1245 _editor->commit_reversible_command ();
1252 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1253 : RegionMoveDrag (e, i, p, v, false, false)
1258 struct RegionSelectionByPosition {
1259 bool operator() (RegionView*a, RegionView* b) {
1260 return a->region()->position () < b->region()->position();
1265 RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
1267 RouteTimeAxisView* tv;
1270 if (!check_possible (&tv, &layer)) {
1276 if (_current_pointer_x - _grab_x > 0) {
1282 RegionSelection copy (_editor->selection->regions);
1284 RegionSelectionByPosition cmp;
1287 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1289 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1295 boost::shared_ptr<Playlist> playlist;
1297 if ((playlist = atv->playlist()) == 0) {
1301 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1306 if (_current_pointer_frame < (*i)->region()->last_frame() + 1) {
1310 if (_current_pointer_frame > (*i)->region()->first_frame()) {
1316 playlist->shuffle ((*i)->region(), dir);
1318 _grab_x = _current_pointer_x;
1323 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1329 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1337 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1339 _dest_trackview = _view;
1341 Drag::start_grab (event);
1346 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1349 // TODO: create region-create-drag region view here
1352 // TODO: resize region-create-drag region view here
1356 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1358 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1364 if (!movement_occurred) {
1365 mtv->add_region (_grab_frame);
1367 motion (event, false);
1368 // TODO: create region-create-drag region here
1372 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1380 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1383 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1385 Drag::start_grab (event);
1387 region = &cnote->region_view();
1389 double region_start = region->get_position_pixels();
1390 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1392 if (_grab_x <= middle_point) {
1393 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1396 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1400 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1402 if (event->motion.state & Keyboard::PrimaryModifier) {
1408 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1410 if (ms.size() > 1) {
1411 /* has to be relative, may make no sense otherwise */
1415 region->note_selected (cnote, true);
1417 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1418 MidiRegionSelection::iterator next;
1421 (*r)->begin_resizing (at_front);
1427 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1429 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1430 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1431 (*r)->update_resizing (at_front, _current_pointer_x - _grab_x, relative);
1436 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1438 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1439 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1440 (*r)->commit_resizing (at_front, _current_pointer_x - _grab_x, relative);
1445 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1451 RegionGainDrag::finished (GdkEvent *, bool)
1456 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1457 : RegionDrag (e, i, p, v)
1463 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1466 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1467 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1469 if (tv && tv->is_track()) {
1470 speed = tv->get_diskstream()->speed();
1473 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1474 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1475 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1477 Drag::start_grab (event, _editor->trimmer_cursor);
1479 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1480 _operation = ContentsTrim;
1482 /* These will get overridden for a point trim.*/
1483 if (_current_pointer_frame < (region_start + region_length/2)) {
1484 /* closer to start */
1485 _operation = StartTrim;
1486 } else if (_current_pointer_frame > (region_end - region_length/2)) {
1488 _operation = EndTrim;
1492 switch (_operation) {
1494 _editor->show_verbose_time_cursor (region_start, 10);
1497 _editor->show_verbose_time_cursor (region_end, 10);
1500 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1506 TrimDrag::motion (GdkEvent* event, bool first_move)
1508 RegionView* rv = _primary;
1509 nframes64_t frame_delta = 0;
1511 bool left_direction;
1512 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1514 /* snap modifier works differently here..
1515 its' current state has to be passed to the
1516 various trim functions in order to work properly
1520 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1521 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1522 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1524 if (tv && tv->is_track()) {
1525 speed = tv->get_diskstream()->speed();
1528 if (_last_pointer_frame > _current_pointer_frame) {
1529 left_direction = true;
1531 left_direction = false;
1534 _editor->snap_to_with_modifier (_current_pointer_frame, event);
1540 switch (_operation) {
1542 trim_type = "Region start trim";
1545 trim_type = "Region end trim";
1548 trim_type = "Region content trim";
1552 _editor->begin_reversible_command (trim_type);
1554 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1555 (*i)->fake_set_opaque(false);
1556 (*i)->region()->freeze ();
1558 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1561 arv->temporarily_hide_envelope ();
1564 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1565 insert_result = _editor->motion_frozen_playlists.insert (pl);
1567 if (insert_result.second) {
1568 _editor->session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1574 if (_current_pointer_frame == _last_pointer_frame) {
1578 if (left_direction) {
1579 frame_delta = (_last_pointer_frame - _current_pointer_frame);
1581 frame_delta = (_current_pointer_frame - _last_pointer_frame);
1584 bool non_overlap_trim = false;
1586 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1587 non_overlap_trim = true;
1590 switch (_operation) {
1592 if ((left_direction == false) && (_current_pointer_frame <= rv->region()->first_frame()/speed)) {
1596 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1597 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1603 if ((left_direction == true) && (_current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
1607 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1608 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1615 bool swap_direction = false;
1617 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1618 swap_direction = true;
1621 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i)
1623 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1629 switch (_operation) {
1631 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1634 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1637 _editor->show_verbose_time_cursor(_current_pointer_frame, 10);
1641 _last_pointer_frame = _current_pointer_frame;
1646 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1648 if (movement_occurred) {
1649 motion (event, false);
1651 if (!_editor->selection->selected (_primary)) {
1652 _editor->thaw_region_after_trim (*_primary);
1655 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1656 _editor->thaw_region_after_trim (**i);
1657 (*i)->fake_set_opaque (true);
1661 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1663 _editor->session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1666 _editor->motion_frozen_playlists.clear ();
1668 _editor->commit_reversible_command();
1670 /* no mouse movement */
1671 _editor->point_trim (event);
1675 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1679 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1684 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1687 // create a dummy marker for visual representation of moving the copy.
1688 // The actual copying is not done before we reach the finish callback.
1690 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1691 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1692 *new MeterSection (_marker->meter()));
1694 _item = &new_marker->the_item ();
1695 _marker = new_marker;
1699 MetricSection& section (_marker->meter());
1701 if (!section.movable()) {
1707 Drag::start_grab (event, cursor);
1709 _pointer_frame_offset = _grab_frame - _marker->meter().frame();
1711 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1715 MeterMarkerDrag::motion (GdkEvent* event, bool)
1717 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1719 if (adjusted_frame == _last_pointer_frame) {
1723 _marker->set_position (adjusted_frame);
1725 _last_pointer_frame = adjusted_frame;
1727 _editor->show_verbose_time_cursor (adjusted_frame, 10);
1731 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1733 if (!movement_occurred) {
1737 motion (event, false);
1741 TempoMap& map (_editor->session->tempo_map());
1742 map.bbt_time (_last_pointer_frame, when);
1744 if (_copy == true) {
1745 _editor->begin_reversible_command (_("copy meter mark"));
1746 XMLNode &before = map.get_state();
1747 map.add_meter (_marker->meter(), when);
1748 XMLNode &after = map.get_state();
1749 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1750 _editor->commit_reversible_command ();
1752 // delete the dummy marker we used for visual representation of copying.
1753 // a new visual marker will show up automatically.
1756 _editor->begin_reversible_command (_("move meter mark"));
1757 XMLNode &before = map.get_state();
1758 map.move_meter (_marker->meter(), when);
1759 XMLNode &after = map.get_state();
1760 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1761 _editor->commit_reversible_command ();
1765 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1769 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1774 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1779 // create a dummy marker for visual representation of moving the copy.
1780 // The actual copying is not done before we reach the finish callback.
1782 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1783 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1784 *new TempoSection (_marker->tempo()));
1786 _item = &new_marker->the_item ();
1787 _marker = new_marker;
1791 MetricSection& section (_marker->tempo());
1793 if (!section.movable()) {
1798 Drag::start_grab (event, cursor);
1800 _pointer_frame_offset = _grab_frame - _marker->tempo().frame();
1801 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1805 TempoMarkerDrag::motion (GdkEvent* event, bool)
1807 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1809 if (adjusted_frame == _last_pointer_frame) {
1813 /* OK, we've moved far enough to make it worth actually move the thing. */
1815 _marker->set_position (adjusted_frame);
1817 _editor->show_verbose_time_cursor (adjusted_frame, 10);
1819 _last_pointer_frame = adjusted_frame;
1823 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1825 if (!movement_occurred) {
1829 motion (event, false);
1833 TempoMap& map (_editor->session->tempo_map());
1834 map.bbt_time (_last_pointer_frame, when);
1836 if (_copy == true) {
1837 _editor->begin_reversible_command (_("copy tempo mark"));
1838 XMLNode &before = map.get_state();
1839 map.add_tempo (_marker->tempo(), when);
1840 XMLNode &after = map.get_state();
1841 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1842 _editor->commit_reversible_command ();
1844 // delete the dummy marker we used for visual representation of copying.
1845 // a new visual marker will show up automatically.
1848 _editor->begin_reversible_command (_("move tempo mark"));
1849 XMLNode &before = map.get_state();
1850 map.move_tempo (_marker->tempo(), when);
1851 XMLNode &after = map.get_state();
1852 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1853 _editor->commit_reversible_command ();
1858 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1862 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1867 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1869 Drag::start_grab (event, c);
1873 nframes64_t where = _editor->event_frame (event, 0, 0);
1875 _editor->snap_to_with_modifier (where, event);
1876 _editor->playhead_cursor->set_position (where);
1880 if (_cursor == _editor->playhead_cursor) {
1881 _editor->_dragging_playhead = true;
1883 if (_editor->session && _was_rolling && _stop) {
1884 _editor->session->request_stop ();
1887 if (_editor->session && _editor->session->is_auditioning()) {
1888 _editor->session->cancel_audition ();
1892 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1896 CursorDrag::motion (GdkEvent* event, bool)
1898 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1900 if (adjusted_frame == _last_pointer_frame) {
1904 _cursor->set_position (adjusted_frame);
1906 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1909 _editor->update_canvas_now ();
1911 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1913 _last_pointer_frame = adjusted_frame;
1917 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1919 _editor->_dragging_playhead = false;
1921 if (!movement_occurred && _stop) {
1925 motion (event, false);
1927 if (_item == &_editor->playhead_cursor->canvas_item) {
1928 if (_editor->session) {
1929 _editor->session->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1930 _editor->_pending_locate_request = true;
1935 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1936 : RegionDrag (e, i, p, v)
1942 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1944 Drag::start_grab (event, cursor);
1946 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1947 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1949 _pointer_frame_offset = _grab_frame - ((nframes64_t) r->fade_in()->back()->when + r->position());
1953 FadeInDrag::motion (GdkEvent* event, bool)
1955 nframes64_t fade_length;
1957 nframes64_t const pos = adjusted_current_frame (event);
1959 boost::shared_ptr<Region> region = _primary->region ();
1961 if (pos < (region->position() + 64)) {
1962 fade_length = 64; // this should be a minimum defined somewhere
1963 } else if (pos > region->last_frame()) {
1964 fade_length = region->length();
1966 fade_length = pos - region->position();
1969 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1971 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1977 tmp->reset_fade_in_shape_width (fade_length);
1980 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
1984 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
1986 if (!movement_occurred) {
1990 nframes64_t fade_length;
1992 nframes64_t const pos = adjusted_current_frame (event);
1994 boost::shared_ptr<Region> region = _primary->region ();
1996 if (pos < (region->position() + 64)) {
1997 fade_length = 64; // this should be a minimum defined somewhere
1998 } else if (pos > region->last_frame()) {
1999 fade_length = region->length();
2001 fade_length = pos - region->position();
2004 _editor->begin_reversible_command (_("change fade in length"));
2006 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2008 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2014 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2015 XMLNode &before = alist->get_state();
2017 tmp->audio_region()->set_fade_in_length (fade_length);
2018 tmp->audio_region()->set_fade_in_active (true);
2020 XMLNode &after = alist->get_state();
2021 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2024 _editor->commit_reversible_command ();
2027 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2028 : RegionDrag (e, i, p, v)
2034 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2036 Drag::start_grab (event, cursor);
2038 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2039 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2041 _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2045 FadeOutDrag::motion (GdkEvent* event, bool)
2047 nframes64_t fade_length;
2049 nframes64_t const pos = adjusted_current_frame (event);
2051 boost::shared_ptr<Region> region = _primary->region ();
2053 if (pos > (region->last_frame() - 64)) {
2054 fade_length = 64; // this should really be a minimum fade defined somewhere
2056 else if (pos < region->position()) {
2057 fade_length = region->length();
2060 fade_length = region->last_frame() - pos;
2063 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2065 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2071 tmp->reset_fade_out_shape_width (fade_length);
2074 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2078 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2080 if (!movement_occurred) {
2084 nframes64_t fade_length;
2086 nframes64_t const pos = adjusted_current_frame (event);
2088 boost::shared_ptr<Region> region = _primary->region ();
2090 if (pos > (region->last_frame() - 64)) {
2091 fade_length = 64; // this should really be a minimum fade defined somewhere
2093 else if (pos < region->position()) {
2094 fade_length = region->length();
2097 fade_length = region->last_frame() - pos;
2100 _editor->begin_reversible_command (_("change fade out length"));
2102 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2104 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2110 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2111 XMLNode &before = alist->get_state();
2113 tmp->audio_region()->set_fade_out_length (fade_length);
2114 tmp->audio_region()->set_fade_out_active (true);
2116 XMLNode &after = alist->get_state();
2117 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2120 _editor->commit_reversible_command ();
2123 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2126 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2129 _points.push_back (Gnome::Art::Point (0, 0));
2130 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2132 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2133 _line->property_width_pixels() = 1;
2134 _line->property_points () = _points;
2137 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2140 MarkerDrag::~MarkerDrag ()
2142 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2148 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2150 Drag::start_grab (event, cursor);
2154 Location *location = _editor->find_location_from_marker (_marker, is_start);
2155 _editor->_dragging_edit_point = true;
2157 _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end());
2159 update_item (location);
2161 // _drag_line->show();
2162 // _line->raise_to_top();
2165 _editor->show_verbose_time_cursor (location->start(), 10);
2167 _editor->show_verbose_time_cursor (location->end(), 10);
2170 Selection::Operation op = Keyboard::selection_type (event->button.state);
2173 case Selection::Toggle:
2174 _editor->selection->toggle (_marker);
2176 case Selection::Set:
2177 if (!_editor->selection->selected (_marker)) {
2178 _editor->selection->set (_marker);
2181 case Selection::Extend:
2183 Locations::LocationList ll;
2184 list<Marker*> to_add;
2186 _editor->selection->markers.range (s, e);
2187 s = min (_marker->position(), s);
2188 e = max (_marker->position(), e);
2191 if (e < max_frames) {
2194 _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2195 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2196 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2199 to_add.push_back (lm->start);
2202 to_add.push_back (lm->end);
2206 if (!to_add.empty()) {
2207 _editor->selection->add (to_add);
2211 case Selection::Add:
2212 _editor->selection->add (_marker);
2216 /* set up copies for us to manipulate during the drag */
2218 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2219 Location *l = _editor->find_location_from_marker (*i, is_start);
2220 _copied_locations.push_back (new Location (*l));
2225 MarkerDrag::motion (GdkEvent* event, bool)
2227 nframes64_t f_delta = 0;
2229 bool move_both = false;
2231 Location *real_location;
2232 Location *copy_location = 0;
2234 nframes64_t const newframe = adjusted_current_frame (event);
2236 nframes64_t next = newframe;
2238 if (_current_pointer_frame == _last_pointer_frame) {
2242 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2246 MarkerSelection::iterator i;
2247 list<Location*>::iterator x;
2249 /* find the marker we're dragging, and compute the delta */
2251 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2252 x != _copied_locations.end() && i != _editor->selection->markers.end();
2258 if (marker == _marker) {
2260 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2265 if (real_location->is_mark()) {
2266 f_delta = newframe - copy_location->start();
2270 switch (marker->type()) {
2272 case Marker::LoopStart:
2273 case Marker::PunchIn:
2274 f_delta = newframe - copy_location->start();
2278 case Marker::LoopEnd:
2279 case Marker::PunchOut:
2280 f_delta = newframe - copy_location->end();
2283 /* what kind of marker is this ? */
2291 if (i == _editor->selection->markers.end()) {
2292 /* hmm, impossible - we didn't find the dragged marker */
2296 /* now move them all */
2298 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2299 x != _copied_locations.end() && i != _editor->selection->markers.end();
2305 /* call this to find out if its the start or end */
2307 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2311 if (real_location->locked()) {
2315 if (copy_location->is_mark()) {
2319 copy_location->set_start (copy_location->start() + f_delta);
2323 nframes64_t new_start = copy_location->start() + f_delta;
2324 nframes64_t new_end = copy_location->end() + f_delta;
2326 if (is_start) { // start-of-range marker
2329 copy_location->set_start (new_start);
2330 copy_location->set_end (new_end);
2331 } else if (new_start < copy_location->end()) {
2332 copy_location->set_start (new_start);
2334 _editor->snap_to (next, 1, true);
2335 copy_location->set_end (next);
2336 copy_location->set_start (newframe);
2339 } else { // end marker
2342 copy_location->set_end (new_end);
2343 copy_location->set_start (new_start);
2344 } else if (new_end > copy_location->start()) {
2345 copy_location->set_end (new_end);
2346 } else if (newframe > 0) {
2347 _editor->snap_to (next, -1, true);
2348 copy_location->set_start (next);
2349 copy_location->set_end (newframe);
2354 update_item (copy_location);
2356 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2359 lm->set_position (copy_location->start(), copy_location->end());
2363 _last_pointer_frame = _current_pointer_frame;
2365 assert (!_copied_locations.empty());
2367 _editor->edit_point_clock.set (_copied_locations.front()->start());
2368 _editor->show_verbose_time_cursor (newframe, 10);
2371 _editor->update_canvas_now ();
2373 _editor->edit_point_clock.set (copy_location->start());
2377 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2379 if (!movement_occurred) {
2381 /* just a click, do nothing but finish
2382 off the selection process
2385 Selection::Operation op = Keyboard::selection_type (event->button.state);
2388 case Selection::Set:
2389 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2390 _editor->selection->set (_marker);
2394 case Selection::Toggle:
2395 case Selection::Extend:
2396 case Selection::Add:
2403 _editor->_dragging_edit_point = false;
2405 _editor->begin_reversible_command ( _("move marker") );
2406 XMLNode &before = _editor->session->locations()->get_state();
2408 MarkerSelection::iterator i;
2409 list<Location*>::iterator x;
2412 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2413 x != _copied_locations.end() && i != _editor->selection->markers.end();
2416 Location * location = _editor->find_location_from_marker (*i, is_start);
2420 if (location->locked()) {
2424 if (location->is_mark()) {
2425 location->set_start ((*x)->start());
2427 location->set ((*x)->start(), (*x)->end());
2432 XMLNode &after = _editor->session->locations()->get_state();
2433 _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
2434 _editor->commit_reversible_command ();
2440 MarkerDrag::update_item (Location* location)
2442 double const x1 = _editor->frame_to_pixel (location->start());
2444 _points.front().set_x(x1);
2445 _points.back().set_x(x1);
2446 _line->property_points() = _points;
2449 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2451 _cumulative_x_drag (0),
2452 _cumulative_y_drag (0)
2454 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2460 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2462 Drag::start_grab (event, _editor->fader_cursor);
2464 // start the grab at the center of the control point so
2465 // the point doesn't 'jump' to the mouse after the first drag
2466 _grab_x = _point->get_x();
2467 _grab_y = _point->get_y();
2469 _point->line().parent_group().i2w (_grab_x, _grab_y);
2470 _editor->track_canvas->w2c (_grab_x, _grab_y, _grab_x, _grab_y);
2472 _grab_frame = _editor->pixel_to_frame (_grab_x);
2474 _point->line().start_drag (_point, _grab_frame, 0);
2476 float fraction = 1.0 - (_point->get_y() / _point->line().height());
2477 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2478 _current_pointer_x + 10, _current_pointer_y + 10);
2480 _editor->show_verbose_canvas_cursor ();
2484 ControlPointDrag::motion (GdkEvent* event, bool)
2486 double dx = _current_pointer_x - _last_pointer_x;
2487 double dy = _current_pointer_y - _last_pointer_y;
2489 if (event->button.state & Keyboard::SecondaryModifier) {
2494 double cx = _grab_x + _cumulative_x_drag + dx;
2495 double cy = _grab_y + _cumulative_y_drag + dy;
2497 // calculate zero crossing point. back off by .01 to stay on the
2498 // positive side of zero
2500 double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2501 _point->line().parent_group().i2w(_unused, zero_gain_y);
2503 // make sure we hit zero when passing through
2504 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2505 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2509 if (_x_constrained) {
2512 if (_y_constrained) {
2516 _cumulative_x_drag = cx - _grab_x;
2517 _cumulative_y_drag = cy - _grab_y;
2519 _point->line().parent_group().w2i (cx, cy);
2523 cy = min ((double) _point->line().height(), cy);
2525 //translate cx to frames
2526 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2528 if (!_x_constrained) {
2529 _editor->snap_to_with_modifier (cx_frames, event);
2532 float const fraction = 1.0 - (cy / _point->line().height());
2534 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2536 _point->line().point_drag (*_point, cx_frames, fraction, push);
2538 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2542 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2544 if (!movement_occurred) {
2548 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2549 _editor->reset_point_selection ();
2553 motion (event, false);
2555 _point->line().end_drag (_point);
2559 ControlPointDrag::active (Editing::MouseMode m)
2561 if (m == Editing::MouseGain) {
2562 /* always active in mouse gain */
2566 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2567 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2570 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2573 _cumulative_y_drag (0)
2578 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2580 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2583 _item = &_line->grab_item ();
2585 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2586 origin, and ditto for y.
2589 double cx = event->button.x;
2590 double cy = event->button.y;
2592 _line->parent_group().w2i (cx, cy);
2594 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2596 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
2597 /* no adjacent points */
2601 Drag::start_grab (event, _editor->fader_cursor);
2603 /* store grab start in parent frame */
2608 double fraction = 1.0 - (cy / _line->height());
2610 _line->start_drag (0, _grab_frame, fraction);
2612 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2613 _current_pointer_x + 10, _current_pointer_y + 10);
2615 _editor->show_verbose_canvas_cursor ();
2619 LineDrag::motion (GdkEvent* event, bool)
2621 double dy = _current_pointer_y - _last_pointer_y;
2623 if (event->button.state & Keyboard::SecondaryModifier) {
2627 double cy = _grab_y + _cumulative_y_drag + dy;
2629 _cumulative_y_drag = cy - _grab_y;
2632 cy = min ((double) _line->height(), cy);
2634 double const fraction = 1.0 - (cy / _line->height());
2638 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2644 _line->line_drag (_before, _after, fraction, push);
2646 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2650 LineDrag::finished (GdkEvent* event, bool)
2652 motion (event, false);
2653 _line->end_drag (0);
2657 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2659 Drag::start_grab (event);
2660 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2664 RubberbandSelectDrag::motion (GdkEvent* event, bool first_move)
2671 /* use a bigger drag threshold than the default */
2673 if (abs ((int) (_current_pointer_frame - _grab_frame)) < 8) {
2677 if (Config->get_rubberbanding_snaps_to_grid()) {
2679 _editor->snap_to_with_modifier (_grab_frame, event);
2681 _editor->snap_to_with_modifier (_current_pointer_frame, event);
2684 /* base start and end on initial click position */
2686 if (_current_pointer_frame < _grab_frame) {
2687 start = _current_pointer_frame;
2690 end = _current_pointer_frame;
2691 start = _grab_frame;
2694 if (_current_pointer_y < _grab_y) {
2695 y1 = _current_pointer_y;
2698 y2 = _current_pointer_y;
2703 if (start != end || y1 != y2) {
2705 double x1 = _editor->frame_to_pixel (start);
2706 double x2 = _editor->frame_to_pixel (end);
2708 _editor->rubberband_rect->property_x1() = x1;
2709 _editor->rubberband_rect->property_y1() = y1;
2710 _editor->rubberband_rect->property_x2() = x2;
2711 _editor->rubberband_rect->property_y2() = y2;
2713 _editor->rubberband_rect->show();
2714 _editor->rubberband_rect->raise_to_top();
2716 _last_pointer_frame = _current_pointer_frame;
2718 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2723 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2725 if (movement_occurred) {
2727 motion (event, false);
2730 if (_current_pointer_y < _grab_y) {
2731 y1 = _current_pointer_y;
2734 y2 = _current_pointer_y;
2739 Selection::Operation op = Keyboard::selection_type (event->button.state);
2742 _editor->begin_reversible_command (_("rubberband selection"));
2744 if (_grab_frame < _last_pointer_frame) {
2745 committed = _editor->select_all_within (_grab_frame, _last_pointer_frame - 1, y1, y2, _editor->track_views, op);
2747 committed = _editor->select_all_within (_last_pointer_frame, _grab_frame - 1, y1, y2, _editor->track_views, op);
2751 _editor->commit_reversible_command ();
2755 if (!getenv("ARDOUR_SAE")) {
2756 _editor->selection->clear_tracks();
2758 _editor->selection->clear_regions();
2759 _editor->selection->clear_points ();
2760 _editor->selection->clear_lines ();
2763 _editor->rubberband_rect->hide();
2767 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2769 Drag::start_grab (event);
2771 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2775 TimeFXDrag::motion (GdkEvent* event, bool)
2777 RegionView* rv = _primary;
2779 _editor->snap_to_with_modifier (_current_pointer_frame, event);
2781 if (_current_pointer_frame == _last_pointer_frame) {
2785 if (_current_pointer_frame > rv->region()->position()) {
2786 rv->get_time_axis_view().show_timestretch (rv->region()->position(), _current_pointer_frame);
2789 _last_pointer_frame = _current_pointer_frame;
2791 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2795 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2797 _primary->get_time_axis_view().hide_timestretch ();
2799 if (!movement_occurred) {
2803 if (_last_pointer_frame < _primary->region()->position()) {
2804 /* backwards drag of the left edge - not usable */
2808 nframes64_t newlen = _last_pointer_frame - _primary->region()->position();
2810 float percentage = (double) newlen / (double) _primary->region()->length();
2812 #ifndef USE_RUBBERBAND
2813 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2814 if (_primary->region()->data_type() == DataType::AUDIO) {
2815 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2819 _editor->begin_reversible_command (_("timestretch"));
2821 // XXX how do timeFX on multiple regions ?
2826 if (_editor->time_stretch (rs, percentage) == 0) {
2827 _editor->session->commit_reversible_command ();
2832 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2834 Drag::start_grab (event);
2838 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2844 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2846 if (movement_occurred && _editor->session) {
2847 /* make sure we stop */
2848 _editor->session->request_transport_speed (0.0);
2852 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2861 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2863 nframes64_t start = 0;
2864 nframes64_t end = 0;
2866 if (_editor->session == 0) {
2870 Gdk::Cursor* cursor = 0;
2872 switch (_operation) {
2873 case CreateSelection:
2874 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2879 cursor = _editor->selector_cursor;
2880 Drag::start_grab (event, cursor);
2883 case SelectionStartTrim:
2884 if (_editor->clicked_axisview) {
2885 _editor->clicked_axisview->order_selection_trims (_item, true);
2887 Drag::start_grab (event, cursor);
2888 cursor = _editor->trimmer_cursor;
2889 start = _editor->selection->time[_editor->clicked_selection].start;
2890 _pointer_frame_offset = _grab_frame - start;
2893 case SelectionEndTrim:
2894 if (_editor->clicked_axisview) {
2895 _editor->clicked_axisview->order_selection_trims (_item, false);
2897 Drag::start_grab (event, cursor);
2898 cursor = _editor->trimmer_cursor;
2899 end = _editor->selection->time[_editor->clicked_selection].end;
2900 _pointer_frame_offset = _grab_frame - end;
2904 start = _editor->selection->time[_editor->clicked_selection].start;
2905 Drag::start_grab (event, cursor);
2906 _pointer_frame_offset = _grab_frame - start;
2910 if (_operation == SelectionMove) {
2911 _editor->show_verbose_time_cursor (start, 10);
2913 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2918 SelectionDrag::motion (GdkEvent* event, bool first_move)
2920 nframes64_t start = 0;
2921 nframes64_t end = 0;
2924 nframes64_t const pending_position = adjusted_current_frame (event);
2926 /* only alter selection if the current frame is
2927 different from the last frame position (adjusted)
2930 if (pending_position == _last_pointer_frame) {
2934 switch (_operation) {
2935 case CreateSelection:
2938 _editor->snap_to (_grab_frame);
2941 if (pending_position < _grab_frame) {
2942 start = pending_position;
2945 end = pending_position;
2946 start = _grab_frame;
2949 /* first drag: Either add to the selection
2950 or create a new selection->
2955 _editor->begin_reversible_command (_("range selection"));
2958 /* adding to the selection */
2959 _editor->clicked_selection = _editor->selection->add (start, end);
2962 /* new selection-> */
2963 _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end);
2968 case SelectionStartTrim:
2971 _editor->begin_reversible_command (_("trim selection start"));
2974 start = _editor->selection->time[_editor->clicked_selection].start;
2975 end = _editor->selection->time[_editor->clicked_selection].end;
2977 if (pending_position > end) {
2980 start = pending_position;
2984 case SelectionEndTrim:
2987 _editor->begin_reversible_command (_("trim selection end"));
2990 start = _editor->selection->time[_editor->clicked_selection].start;
2991 end = _editor->selection->time[_editor->clicked_selection].end;
2993 if (pending_position < start) {
2996 end = pending_position;
3004 _editor->begin_reversible_command (_("move selection"));
3007 start = _editor->selection->time[_editor->clicked_selection].start;
3008 end = _editor->selection->time[_editor->clicked_selection].end;
3010 length = end - start;
3012 start = pending_position;
3013 _editor->snap_to (start);
3015 end = start + length;
3020 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3021 _editor->start_canvas_autoscroll (1, 0);
3025 _editor->selection->replace (_editor->clicked_selection, start, end);
3028 _last_pointer_frame = pending_position;
3030 if (_operation == SelectionMove) {
3031 _editor->show_verbose_time_cursor(start, 10);
3033 _editor->show_verbose_time_cursor(pending_position, 10);
3038 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3040 if (movement_occurred) {
3041 motion (event, false);
3042 /* XXX this is not object-oriented programming at all. ick */
3043 if (_editor->selection->time.consolidate()) {
3044 _editor->selection->TimeChanged ();
3046 _editor->commit_reversible_command ();
3048 /* just a click, no pointer movement.*/
3050 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3052 _editor->selection->clear_time();
3057 /* XXX what happens if its a music selection? */
3058 _editor->session->set_audio_range (_editor->selection->time);
3059 _editor->stop_canvas_autoscroll ();
3062 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3067 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3068 _drag_rect->hide ();
3070 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3071 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3075 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3077 if (_editor->session == 0) {
3081 Gdk::Cursor* cursor = 0;
3083 if (!_editor->temp_location) {
3084 _editor->temp_location = new Location;
3087 switch (_operation) {
3088 case CreateRangeMarker:
3089 case CreateTransportMarker:
3090 case CreateCDMarker:
3092 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3097 cursor = _editor->selector_cursor;
3101 Drag::start_grab (event, cursor);
3103 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3107 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3109 nframes64_t start = 0;
3110 nframes64_t end = 0;
3111 ArdourCanvas::SimpleRect *crect;
3113 switch (_operation) {
3114 case CreateRangeMarker:
3115 crect = _editor->range_bar_drag_rect;
3117 case CreateTransportMarker:
3118 crect = _editor->transport_bar_drag_rect;
3120 case CreateCDMarker:
3121 crect = _editor->cd_marker_bar_drag_rect;
3124 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3129 _editor->snap_to_with_modifier (_current_pointer_frame, event);
3131 /* only alter selection if the current frame is
3132 different from the last frame position.
3135 if (_current_pointer_frame == _last_pointer_frame) {
3139 switch (_operation) {
3140 case CreateRangeMarker:
3141 case CreateTransportMarker:
3142 case CreateCDMarker:
3144 _editor->snap_to (_grab_frame);
3147 if (_current_pointer_frame < _grab_frame) {
3148 start = _current_pointer_frame;
3151 end = _current_pointer_frame;
3152 start = _grab_frame;
3155 /* first drag: Either add to the selection
3156 or create a new selection.
3161 _editor->temp_location->set (start, end);
3165 update_item (_editor->temp_location);
3167 //_drag_rect->raise_to_top();
3173 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3174 _editor->start_canvas_autoscroll (1, 0);
3178 _editor->temp_location->set (start, end);
3180 double x1 = _editor->frame_to_pixel (start);
3181 double x2 = _editor->frame_to_pixel (end);
3182 crect->property_x1() = x1;
3183 crect->property_x2() = x2;
3185 update_item (_editor->temp_location);
3188 _last_pointer_frame = _current_pointer_frame;
3190 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3195 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3197 Location * newloc = 0;
3201 if (movement_occurred) {
3202 motion (event, false);
3205 switch (_operation) {
3206 case CreateRangeMarker:
3207 case CreateCDMarker:
3209 _editor->begin_reversible_command (_("new range marker"));
3210 XMLNode &before = _editor->session->locations()->get_state();
3211 _editor->session->locations()->next_available_name(rangename,"unnamed");
3212 if (_operation == CreateCDMarker) {
3213 flags = Location::IsRangeMarker | Location::IsCDMarker;
3214 _editor->cd_marker_bar_drag_rect->hide();
3217 flags = Location::IsRangeMarker;
3218 _editor->range_bar_drag_rect->hide();
3220 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3221 _editor->session->locations()->add (newloc, true);
3222 XMLNode &after = _editor->session->locations()->get_state();
3223 _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
3224 _editor->commit_reversible_command ();
3228 case CreateTransportMarker:
3229 // popup menu to pick loop or punch
3230 _editor->new_transport_marker_context_menu (&event->button, _item);
3234 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3236 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3241 _editor->session->locations()->marks_either_side (_grab_frame, start, end);
3243 if (end == max_frames) {
3244 end = _editor->session->current_end_frame ();
3247 if (start == max_frames) {
3248 start = _editor->session->current_start_frame ();
3251 switch (_editor->mouse_mode) {
3253 /* find the two markers on either side and then make the selection from it */
3254 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3258 /* find the two markers on either side of the click and make the range out of it */
3259 _editor->selection->set (0, start, end);
3268 _editor->stop_canvas_autoscroll ();
3274 RangeMarkerBarDrag::update_item (Location* location)
3276 double const x1 = _editor->frame_to_pixel (location->start());
3277 double const x2 = _editor->frame_to_pixel (location->end());
3279 _drag_rect->property_x1() = x1;
3280 _drag_rect->property_x2() = x2;
3284 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3286 Drag::start_grab (event, _editor->zoom_cursor);
3287 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3291 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3296 _editor->snap_to_with_modifier (_current_pointer_frame, event);
3299 _editor->snap_to_with_modifier (_grab_frame, event);
3302 if (_current_pointer_frame == _last_pointer_frame) {
3306 /* base start and end on initial click position */
3307 if (_current_pointer_frame < _grab_frame) {
3308 start = _current_pointer_frame;
3311 end = _current_pointer_frame;
3312 start = _grab_frame;
3318 _editor->zoom_rect->show();
3319 _editor->zoom_rect->raise_to_top();
3322 _editor->reposition_zoom_rect(start, end);
3324 _last_pointer_frame = _current_pointer_frame;
3326 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3331 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3333 if (movement_occurred) {
3334 motion (event, false);
3336 if (_grab_frame < _last_pointer_frame) {
3337 _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");
3339 _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom");
3342 _editor->temporal_zoom_to_frame (false, _grab_frame);
3344 temporal_zoom_step (false);
3345 center_screen (_grab_frame);
3349 _editor->zoom_rect->hide();
3352 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3355 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3356 region = &cnote->region_view();
3360 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3362 Drag::start_grab (event);
3365 drag_delta_note = 0;
3370 event_x = _current_pointer_x;
3371 event_y = _current_pointer_y;
3373 _item->property_parent().get_value()->w2i(event_x, event_y);
3375 last_x = region->snap_to_pixel(event_x);
3378 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3380 if (!(was_selected = cnote->selected())) {
3382 /* tertiary-click means extend selection - we'll do that on button release,
3383 so don't add it here, because otherwise we make it hard to figure
3384 out the "extend-to" range.
3387 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3390 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3393 region->note_selected (cnote, true);
3395 region->unique_select (cnote);
3402 NoteDrag::motion (GdkEvent*, bool)
3404 MidiStreamView* streamview = region->midi_stream_view();
3408 event_x = _current_pointer_x;
3409 event_y = _current_pointer_y;
3411 _item->property_parent().get_value()->w2i(event_x, event_y);
3413 event_x = region->snap_to_pixel(event_x);
3415 double dx = event_x - last_x;
3416 double dy = event_y - last_y;
3421 // Snap to note rows
3423 if (abs (dy) < streamview->note_height()) {
3426 int8_t this_delta_note;
3428 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3430 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3432 drag_delta_note -= this_delta_note;
3433 dy = streamview->note_height() * this_delta_note;
3434 last_y = last_y + dy;
3438 region->move_selection (dx, dy);
3440 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3442 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3443 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3444 _editor->show_verbose_canvas_cursor_with (buf);
3449 NoteDrag::finished (GdkEvent* ev, bool moved)
3451 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3454 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3457 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3459 region->note_deselected (cnote);
3462 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3463 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3465 if (!extend && !add && region->selection_size() > 1) {
3466 region->unique_select(cnote);
3467 } else if (extend) {
3468 region->note_selected (cnote, true, true);
3470 /* it was added during button press */
3475 region->note_dropped (cnote, drag_delta_x, drag_delta_note);