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;
49 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
51 Drag::Drag (Editor* e, ArdourCanvas::Item* i) :
54 _pointer_frame_offset (0),
56 _last_pointer_frame (0),
57 _current_pointer_frame (0),
58 _had_movement (false),
59 _move_threshold_passed (false)
65 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
71 cursor = _editor->which_grabber_cursor ();
74 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
78 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
81 cursor = _editor->which_grabber_cursor ();
84 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
86 if (Keyboard::is_button2_event (&event->button)) {
87 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
88 _y_constrained = true;
89 _x_constrained = false;
91 _y_constrained = false;
92 _x_constrained = true;
95 _x_constrained = false;
96 _y_constrained = false;
99 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
100 _last_pointer_frame = _grab_frame;
101 _current_pointer_frame = _grab_frame;
102 _current_pointer_x = _grab_x;
103 _current_pointer_y = _grab_y;
104 _last_pointer_x = _current_pointer_x;
105 _last_pointer_y = _current_pointer_y;
109 _item->i2w (_original_x, _original_y);
111 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
115 if (_editor->session && _editor->session->transport_rolling()) {
118 _was_rolling = false;
121 switch (_editor->snap_type) {
122 case SnapToRegionStart:
123 case SnapToRegionEnd:
124 case SnapToRegionSync:
125 case SnapToRegionBoundary:
126 _editor->build_region_boundary_cache ();
133 /** @param event GDK event, or 0.
134 * @return true if some movement occurred, otherwise false.
137 Drag::end_grab (GdkEvent* event)
141 _editor->stop_canvas_autoscroll ();
143 _item->ungrab (event ? event->button.time : 0);
145 _last_pointer_x = _current_pointer_x;
146 _last_pointer_y = _current_pointer_y;
147 finished (event, _had_movement);
149 _editor->hide_verbose_canvas_cursor();
153 return _had_movement;
157 Drag::adjusted_current_frame (GdkEvent* event) const
161 if (_current_pointer_frame > _pointer_frame_offset) {
162 pos = _current_pointer_frame - _pointer_frame_offset;
165 _editor->snap_to_with_modifier (pos, event);
171 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
173 _last_pointer_x = _current_pointer_x;
174 _last_pointer_y = _current_pointer_y;
175 _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
177 if (!from_autoscroll && !_move_threshold_passed) {
179 bool const xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
180 bool const yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
182 _move_threshold_passed = (xp || yp);
184 if (apply_move_threshold() && _move_threshold_passed) {
186 _grab_frame = _current_pointer_frame;
187 _grab_x = _current_pointer_x;
188 _grab_y = _current_pointer_y;
189 _last_pointer_frame = _grab_frame;
190 _pointer_frame_offset = _grab_frame - _last_frame_position;
195 bool old_had_movement = _had_movement;
197 /* a motion event has happened, so we've had movement... */
198 _had_movement = true;
200 /* ... unless we're using a move threshold and we've not yet passed it */
201 if (apply_move_threshold() && !_move_threshold_passed) {
202 _had_movement = false;
205 if (active (_editor->mouse_mode)) {
207 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
208 if (!from_autoscroll) {
209 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
212 motion (event, _had_movement != old_had_movement);
224 _editor->stop_canvas_autoscroll ();
225 _editor->hide_verbose_canvas_cursor ();
230 /* put it back where it came from */
235 _item->i2w (cxw, cyw);
236 _item->move (_original_x - cxw, _original_y - cyw);
241 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
246 RegionView::RegionViewGoingAway.connect (mem_fun (*this, &RegionDrag::region_going_away));
250 RegionDrag::region_going_away (RegionView* v)
255 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
256 : RegionDrag (e, i, p, v),
266 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
268 Drag::start_grab (event);
270 _editor->show_verbose_time_cursor (_last_frame_position, 10);
273 RegionMotionDrag::TimeAxisViewSummary
274 RegionMotionDrag::get_time_axis_view_summary ()
276 int32_t children = 0;
277 TimeAxisViewSummary sum;
279 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
281 /* get a bitmask representing the visible tracks */
283 for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
284 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
285 TimeAxisView::Children children_list;
287 /* zeroes are audio/MIDI tracks. ones are other types. */
289 if (!rtv->hidden()) {
291 if (!rtv->is_track()) {
292 /* not an audio nor MIDI track */
293 sum.tracks = sum.tracks |= (0x01 << rtv->order());
296 sum.height_list[rtv->order()] = (*i)->current_height();
299 if ((children_list = rtv->get_child_list()).size() > 0) {
300 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
301 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
302 sum.height_list[rtv->order() + children] = (*j)->current_height();
313 RegionMotionDrag::compute_y_delta (
314 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
315 int32_t last_pointer_layer, int32_t current_pointer_layer,
316 TimeAxisViewSummary const & tavs,
317 int32_t* pointer_order_span, int32_t* pointer_layer_span,
318 int32_t* canvas_pointer_order_span
322 *pointer_order_span = 0;
323 *pointer_layer_span = 0;
327 bool clamp_y_axis = false;
329 /* the change in track order between this callback and the last */
330 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
331 /* the change in layer between this callback and the last;
332 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
333 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
335 if (*pointer_order_span != 0) {
337 /* find the actual pointer span, in terms of the number of visible tracks;
338 to do this, we reduce |pointer_order_span| by the number of hidden tracks
341 *canvas_pointer_order_span = *pointer_order_span;
342 if (last_pointer_view->order() >= current_pointer_view->order()) {
343 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
344 if (tavs.height_list[y] == 0) {
345 *canvas_pointer_order_span--;
349 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
350 if (tavs.height_list[y] == 0) {
351 *canvas_pointer_order_span++;
356 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
358 RegionView* rv = (*i);
360 if (rv->region()->locked()) {
364 double ix1, ix2, iy1, iy2;
365 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
366 rv->get_canvas_frame()->i2w (ix1, iy1);
367 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
369 /* get the new trackview for this particular region */
370 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
372 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
374 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
375 as surely this is a per-region thing... */
377 clamp_y_axis = y_movement_disallowed (
378 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
386 } else if (_dest_trackview == current_pointer_view) {
388 if (current_pointer_layer == last_pointer_layer) {
389 /* No movement; clamp */
395 _dest_trackview = current_pointer_view;
396 _dest_layer = current_pointer_layer;
404 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
406 *pending_region_position = 0;
408 /* compute the amount of pointer motion in frames, and where
409 the region would be if we moved it by that much.
411 if (_current_pointer_frame >= _pointer_frame_offset) {
413 nframes64_t sync_frame;
414 nframes64_t sync_offset;
417 *pending_region_position = _current_pointer_frame - _pointer_frame_offset;
419 sync_offset = _primary->region()->sync_offset (sync_dir);
421 /* we don't handle a sync point that lies before zero.
423 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
425 sync_frame = *pending_region_position + (sync_dir*sync_offset);
427 _editor->snap_to_with_modifier (sync_frame, event);
429 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
432 *pending_region_position = _last_frame_position;
437 if (*pending_region_position > max_frames - _primary->region()->length()) {
438 *pending_region_position = _last_frame_position;
443 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
445 /* now compute the canvas unit distance we need to move the regionview
446 to make it appear at the new location.
449 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
451 if (*pending_region_position <= _last_frame_position) {
453 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
455 RegionView* rv = (*i);
457 // If any regionview is at zero, we need to know so we can stop further leftward motion.
459 double ix1, ix2, iy1, iy2;
460 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
461 rv->get_canvas_frame()->i2w (ix1, iy1);
463 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
465 *pending_region_position = _last_frame_position;
472 _last_frame_position = *pending_region_position;
479 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
483 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
485 vector<int32_t>::iterator j;
487 /* *pointer* variables reflect things about the pointer; as we may be moving
488 multiple regions, much detail must be computed per-region */
490 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
491 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
492 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
493 is always 0 regardless of what the region's "real" layer is */
494 RouteTimeAxisView* current_pointer_view;
495 layer_t current_pointer_layer;
496 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
500 /* TimeAxisView that we were pointing at last time we entered this method */
501 TimeAxisView const * const last_pointer_view = _dest_trackview;
502 /* the order of the track that we were pointing at last time we entered this method */
503 int32_t const last_pointer_order = last_pointer_view->order ();
504 /* the layer that we were pointing at last time we entered this method */
505 layer_t const last_pointer_layer = _dest_layer;
507 int32_t pointer_order_span;
508 int32_t pointer_layer_span;
509 int32_t canvas_pointer_order_span;
511 bool const clamp_y_axis = compute_y_delta (
512 last_pointer_view, current_pointer_view,
513 last_pointer_layer, current_pointer_layer, tavs,
514 &pointer_order_span, &pointer_layer_span,
515 &canvas_pointer_order_span
518 nframes64_t pending_region_position;
519 double const x_delta = compute_x_delta (event, &pending_region_position);
521 /*************************************************************
523 ************************************************************/
525 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
526 /* haven't reached next snap point, and we're not switching
527 trackviews nor layers. nothing to do.
532 /*************************************************************
534 ************************************************************/
536 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
538 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
540 RegionView* rv = (*i);
542 if (rv->region()->locked()) {
546 /* here we are calculating the y distance from the
547 top of the first track view to the top of the region
548 area of the track view that we're working on */
550 /* this x value is just a dummy value so that we have something
555 /* distance from the top of this track view to the region area
556 of our track view is always 1 */
560 /* convert to world coordinates, ie distance from the top of
563 rv->get_canvas_frame()->i2w (ix1, iy1);
565 /* compensate for the ruler section and the vertical scrollbar position */
566 iy1 += _editor->get_trackview_group_vertical_offset ();
570 // hide any dependent views
572 rv->get_time_axis_view().hide_dependent_views (*rv);
575 reparent to a non scrolling group so that we can keep the
576 region selection above all time axis views.
577 reparenting means we have to move the rv as the two
578 parent groups have different coordinates.
581 rv->get_canvas_group()->property_y() = iy1 - 1;
582 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
584 rv->fake_set_opaque (true);
587 /* current view for this particular region */
588 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
589 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
591 if (pointer_order_span != 0 && !clamp_y_axis) {
593 /* INTER-TRACK MOVEMENT */
595 /* move through the height list to the track that the region is currently on */
596 vector<int32_t>::iterator j = tavs.height_list.begin ();
598 while (j != tavs.height_list.end () && x != rtv->order ()) {
604 int32_t temp_pointer_order_span = canvas_pointer_order_span;
606 if (j != tavs.height_list.end ()) {
608 /* Account for layers in the original and
609 destination tracks. If we're moving around in layers we assume
610 that only one track is involved, so it's ok to use *pointer*
613 StreamView* lv = last_pointer_view->view ();
616 /* move to the top of the last trackview */
617 if (lv->layer_display () == Stacked) {
618 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
621 StreamView* cv = current_pointer_view->view ();
624 /* move to the right layer on the current trackview */
625 if (cv->layer_display () == Stacked) {
626 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
629 /* And for being on a non-topmost layer on the new
632 while (temp_pointer_order_span > 0) {
633 /* we're moving up canvas-wise,
634 so we need to find the next track height
636 if (j != tavs.height_list.begin()) {
640 if (x != last_pointer_order) {
642 ++temp_pointer_order_span;
647 temp_pointer_order_span--;
650 while (temp_pointer_order_span < 0) {
654 if (x != last_pointer_order) {
656 --temp_pointer_order_span;
660 if (j != tavs.height_list.end()) {
664 temp_pointer_order_span++;
668 /* find out where we'll be when we move and set height accordingly */
670 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
671 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
672 rv->set_height (temp_rtv->view()->child_height());
674 /* if you un-comment the following, the region colours will follow
675 the track colours whilst dragging; personally
676 i think this can confuse things, but never mind.
679 //const GdkColor& col (temp_rtv->view->get_region_color());
680 //rv->set_color (const_cast<GdkColor&>(col));
684 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
686 /* INTER-LAYER MOVEMENT in the same track */
687 y_delta = rtv->view()->child_height () * pointer_layer_span;
692 _editor->mouse_brush_insert_region (rv, pending_region_position);
694 rv->move (x_delta, y_delta);
697 } /* foreach region */
700 _editor->cursor_group->raise_to_top();
703 if (x_delta != 0 && !_brushing) {
704 _editor->show_verbose_time_cursor (_last_frame_position, 10);
709 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
711 if (_copy && first_move) {
712 copy_regions (event);
715 RegionMotionDrag::motion (event, first_move);
719 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
721 vector<RegionView*> copies;
722 boost::shared_ptr<Diskstream> ds;
723 boost::shared_ptr<Playlist> from_playlist;
724 RegionSelection new_views;
725 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
726 PlaylistSet modified_playlists;
727 PlaylistSet frozen_playlists;
728 list <sigc::connection> modified_playlist_connections;
729 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
730 nframes64_t drag_delta;
731 bool changed_tracks, changed_position;
732 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
733 RouteTimeAxisView* source_tv;
735 if (!movement_occurred) {
740 if (Config->get_edit_mode() == Splice && !_editor->pre_drag_region_selection.empty()) {
741 _editor->selection->set (_editor->pre_drag_region_selection);
742 _editor->pre_drag_region_selection.clear ();
746 /* all changes were made during motion event handlers */
749 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
750 copies.push_back (*i);
757 /* reverse this here so that we have the correct logic to finalize
761 if (Config->get_edit_mode() == Lock && !_copy) {
762 _x_constrained = !_x_constrained;
766 if (_x_constrained) {
767 _editor->begin_reversible_command (_("fixed time region copy"));
769 _editor->begin_reversible_command (_("region copy"));
772 if (_x_constrained) {
773 _editor->begin_reversible_command (_("fixed time region drag"));
775 _editor->begin_reversible_command (_("region drag"));
779 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
780 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
782 drag_delta = _primary->region()->position() - _last_frame_position;
784 _editor->update_canvas_now ();
786 /* make a list of where each region ended up */
787 final = find_time_axis_views_and_layers ();
789 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
791 RegionView* rv = (*i);
792 RouteTimeAxisView* dest_rtv = final[*i].first;
793 layer_t dest_layer = final[*i].second;
797 if (rv->region()->locked()) {
802 if (changed_position && !_x_constrained) {
803 where = rv->region()->position() - drag_delta;
805 where = rv->region()->position();
808 boost::shared_ptr<Region> new_region;
811 /* we already made a copy */
812 new_region = rv->region();
814 /* undo the previous hide_dependent_views so that xfades don't
815 disappear on copying regions
818 //rv->get_time_axis_view().reveal_dependent_views (*rv);
820 } else if (changed_tracks && dest_rtv->playlist()) {
821 new_region = RegionFactory::create (rv->region());
824 if (changed_tracks || _copy) {
826 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
833 _editor->latest_regionviews.clear ();
835 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*_editor, &Editor::collect_new_region_view));
837 insert_result = modified_playlists.insert (to_playlist);
839 if (insert_result.second) {
840 _editor->session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
843 to_playlist->add_region (new_region, where);
844 if (dest_rtv->view()->layer_display() == Stacked) {
845 new_region->set_layer (dest_layer);
846 new_region->set_pending_explicit_relayer (true);
851 if (!_editor->latest_regionviews.empty()) {
852 // XXX why just the first one ? we only expect one
853 // commented out in nick_m's canvas reworking. is that intended?
854 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
855 new_views.push_back (_editor->latest_regionviews.front());
860 motion on the same track. plonk the previously reparented region
861 back to its original canvas group (its streamview).
862 No need to do anything for copies as they are fake regions which will be deleted.
865 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
866 rv->get_canvas_group()->property_y() = 0;
868 /* just change the model */
870 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
872 if (dest_rtv->view()->layer_display() == Stacked) {
873 rv->region()->set_layer (dest_layer);
874 rv->region()->set_pending_explicit_relayer (true);
877 insert_result = modified_playlists.insert (playlist);
879 if (insert_result.second) {
880 _editor->session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
882 /* freeze to avoid lots of relayering in the case of a multi-region drag */
883 frozen_insert_result = frozen_playlists.insert(playlist);
885 if (frozen_insert_result.second) {
889 rv->region()->set_position (where, (void*) this);
892 if (changed_tracks && !_copy) {
894 /* get the playlist where this drag started. we can't use rv->region()->playlist()
895 because we may have copied the region and it has not been attached to a playlist.
898 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
899 ds = source_tv->get_diskstream();
900 from_playlist = ds->playlist();
904 assert (from_playlist);
906 /* moved to a different audio track, without copying */
908 /* the region that used to be in the old playlist is not
909 moved to the new one - we use a copy of it. as a result,
910 any existing editor for the region should no longer be
914 rv->hide_region_editor();
915 rv->fake_set_opaque (false);
917 /* remove the region from the old playlist */
919 insert_result = modified_playlists.insert (from_playlist);
921 if (insert_result.second) {
922 _editor->session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
925 from_playlist->remove_region (rv->region());
927 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
928 was selected in all of them, then removing it from a playlist will have removed all
929 trace of it from the selection (i.e. there were N regions selected, we removed 1,
930 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
931 corresponding regionview, and the selection is now empty).
933 this could have invalidated any and all iterators into the region selection.
935 the heuristic we use here is: if the region selection is empty, break out of the loop
936 here. if the region selection is not empty, then restart the loop because we know that
937 we must have removed at least the region(view) we've just been working on as well as any
938 that we processed on previous iterations.
940 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
944 if (_views.empty()) {
955 copies.push_back (rv);
959 _editor->selection->add (new_views);
961 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
966 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
967 _editor->session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
970 _editor->commit_reversible_command ();
972 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
979 RegionMoveDrag::x_move_allowed () const
981 if (Config->get_edit_mode() == Lock) {
983 return !_x_constrained;
985 /* in locked edit mode, reverse the usual meaning of _x_constrained */
986 return _x_constrained;
990 return !_x_constrained;
994 RegionInsertDrag::x_move_allowed () const
996 if (Config->get_edit_mode() == Lock) {
997 return _x_constrained;
1000 return !_x_constrained;
1004 RegionMotionDrag::copy_regions (GdkEvent* event)
1006 /* duplicate the regionview(s) and region(s) */
1008 list<RegionView*> new_regionviews;
1010 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1012 RegionView* rv = (*i);
1013 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1014 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1016 const boost::shared_ptr<const Region> original = rv->region();
1017 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1021 boost::shared_ptr<AudioRegion> audioregion_copy
1022 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1023 nrv = new AudioRegionView (*arv, audioregion_copy);
1025 boost::shared_ptr<MidiRegion> midiregion_copy
1026 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1027 nrv = new MidiRegionView (*mrv, midiregion_copy);
1032 nrv->get_canvas_group()->show ();
1033 new_regionviews.push_back (nrv);
1036 if (new_regionviews.empty()) {
1040 /* reflect the fact that we are dragging the copies */
1042 _primary = new_regionviews.front();
1043 _views = new_regionviews;
1045 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1048 sync the canvas to what we think is its current state
1049 without it, the canvas seems to
1050 "forget" to update properly after the upcoming reparent()
1051 ..only if the mouse is in rapid motion at the time of the grab.
1052 something to do with regionview creation raking so long?
1054 _editor->update_canvas_now();
1058 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1060 /* Which trackview is this ? */
1062 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1063 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1064 (*layer) = tvp.second;
1066 if (*tv && (*tv)->layer_display() == Overlaid) {
1070 /* The region motion is only processed if the pointer is over
1074 if (!(*tv) || !(*tv)->is_track()) {
1075 /* To make sure we hide the verbose canvas cursor when the mouse is
1076 not held over and audiotrack.
1078 _editor->hide_verbose_canvas_cursor ();
1085 /** @param new_order New track order.
1086 * @param old_order Old track order.
1087 * @param visible_y_low Lowest visible order.
1088 * @return true if y movement should not happen, otherwise false.
1091 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1093 if (new_order != old_order) {
1095 /* this isn't the pointer track */
1099 /* moving up the canvas */
1100 if ( (new_order - y_span) >= tavs.visible_y_low) {
1104 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1105 int32_t visible_tracks = 0;
1106 while (visible_tracks < y_span ) {
1108 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1109 /* passing through a hidden track */
1114 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1115 /* moving to a non-track; disallow */
1121 /* moving beyond the lowest visible track; disallow */
1125 } else if (y_span < 0) {
1127 /* moving down the canvas */
1128 if ((new_order - y_span) <= tavs.visible_y_high) {
1130 int32_t visible_tracks = 0;
1132 while (visible_tracks > y_span ) {
1135 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1136 /* passing through a hidden track */
1141 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1142 /* moving to a non-track; disallow */
1149 /* moving beyond the highest visible track; disallow */
1156 /* this is the pointer's track */
1158 if ((new_order - y_span) > tavs.visible_y_high) {
1159 /* we will overflow */
1161 } else if ((new_order - y_span) < tavs.visible_y_low) {
1162 /* we will overflow */
1171 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1172 : RegionMotionDrag (e, i, p, v, b),
1175 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1177 _dest_trackview = tv;
1178 if (tv->layer_display() == Overlaid) {
1181 _dest_layer = _primary->region()->layer ();
1185 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1186 if (rtv && rtv->is_track()) {
1187 speed = rtv->get_diskstream()->speed ();
1190 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1194 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1196 RegionMotionDrag::start_grab (event, c);
1198 _pointer_frame_offset = _grab_frame - _last_frame_position;
1201 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1202 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1204 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1205 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1207 _primary = v->view()->create_region_view (r, false, false);
1209 _primary->get_canvas_group()->show ();
1210 _primary->set_position (pos, 0);
1211 _views.push_back (_primary);
1213 _last_frame_position = pos;
1215 _item = _primary->get_canvas_group ();
1216 _dest_trackview = v;
1217 _dest_layer = _primary->region()->layer ();
1220 map<RegionView*, pair<RouteTimeAxisView*, int> >
1221 RegionMotionDrag::find_time_axis_views_and_layers ()
1223 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1225 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1227 double ix1, ix2, iy1, iy2;
1228 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1229 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1230 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1232 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1233 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1241 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1243 _editor->update_canvas_now ();
1245 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1247 RouteTimeAxisView* dest_rtv = final[_primary].first;
1249 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1250 _primary->get_canvas_group()->property_y() = 0;
1252 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1254 _editor->begin_reversible_command (_("insert region"));
1255 XMLNode& before = playlist->get_state ();
1256 playlist->add_region (_primary->region (), _last_frame_position);
1257 _editor->session->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1258 _editor->commit_reversible_command ();
1265 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1266 : RegionMoveDrag (e, i, p, v, false, false)
1271 struct RegionSelectionByPosition {
1272 bool operator() (RegionView*a, RegionView* b) {
1273 return a->region()->position () < b->region()->position();
1278 RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
1280 RouteTimeAxisView* tv;
1283 if (!check_possible (&tv, &layer)) {
1289 if (_current_pointer_x - _grab_x > 0) {
1295 RegionSelection copy (_editor->selection->regions);
1297 RegionSelectionByPosition cmp;
1300 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1302 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1308 boost::shared_ptr<Playlist> playlist;
1310 if ((playlist = atv->playlist()) == 0) {
1314 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1319 if (_current_pointer_frame < (*i)->region()->last_frame() + 1) {
1323 if (_current_pointer_frame > (*i)->region()->first_frame()) {
1329 playlist->shuffle ((*i)->region(), dir);
1331 _grab_x = _current_pointer_x;
1336 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1342 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1350 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1352 _dest_trackview = _view;
1354 Drag::start_grab (event);
1359 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1362 // TODO: create region-create-drag region view here
1365 // TODO: resize region-create-drag region view here
1369 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1371 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1377 if (!movement_occurred) {
1378 mtv->add_region (_grab_frame);
1380 motion (event, false);
1381 // TODO: create region-create-drag region here
1385 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1393 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1396 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1398 Drag::start_grab (event);
1400 region = &cnote->region_view();
1402 double region_start = region->get_position_pixels();
1403 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1405 if (_grab_x <= middle_point) {
1406 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1409 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1413 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1415 if (event->motion.state & Keyboard::PrimaryModifier) {
1421 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1423 if (ms.size() > 1) {
1424 /* has to be relative, may make no sense otherwise */
1428 region->note_selected (cnote, true);
1430 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1431 MidiRegionSelection::iterator next;
1434 (*r)->begin_resizing (at_front);
1440 NoteResizeDrag::motion (GdkEvent* /*event*/, bool first_move)
1442 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1443 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end();) {
1444 MidiRegionSelection::iterator next;
1447 (*r)->update_resizing (at_front, _current_pointer_x - _grab_x, relative);
1453 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
1455 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1456 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end();) {
1457 MidiRegionSelection::iterator next;
1460 (*r)->commit_resizing (at_front, _current_pointer_x - _grab_x, relative);
1466 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1472 RegionGainDrag::finished (GdkEvent *, bool)
1477 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1478 : RegionDrag (e, i, p, v)
1484 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1487 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1488 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1490 if (tv && tv->is_track()) {
1491 speed = tv->get_diskstream()->speed();
1494 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1495 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1496 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1498 Drag::start_grab (event, _editor->trimmer_cursor);
1500 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1501 _operation = ContentsTrim;
1503 /* These will get overridden for a point trim.*/
1504 if (_current_pointer_frame < (region_start + region_length/2)) {
1505 /* closer to start */
1506 _operation = StartTrim;
1507 } else if (_current_pointer_frame > (region_end - region_length/2)) {
1509 _operation = EndTrim;
1513 switch (_operation) {
1515 _editor->show_verbose_time_cursor (region_start, 10);
1518 _editor->show_verbose_time_cursor (region_end, 10);
1521 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1527 TrimDrag::motion (GdkEvent* event, bool first_move)
1529 RegionView* rv = _primary;
1530 nframes64_t frame_delta = 0;
1532 bool left_direction;
1533 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1535 /* snap modifier works differently here..
1536 its' current state has to be passed to the
1537 various trim functions in order to work properly
1541 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1542 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1543 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1545 if (tv && tv->is_track()) {
1546 speed = tv->get_diskstream()->speed();
1549 if (_last_pointer_frame > _current_pointer_frame) {
1550 left_direction = true;
1552 left_direction = false;
1555 _editor->snap_to_with_modifier (_current_pointer_frame, event);
1561 switch (_operation) {
1563 trim_type = "Region start trim";
1566 trim_type = "Region end trim";
1569 trim_type = "Region content trim";
1573 _editor->begin_reversible_command (trim_type);
1575 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1576 (*i)->fake_set_opaque(false);
1577 (*i)->region()->freeze ();
1579 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1582 arv->temporarily_hide_envelope ();
1585 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1586 insert_result = _editor->motion_frozen_playlists.insert (pl);
1588 if (insert_result.second) {
1589 _editor->session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1595 if (_current_pointer_frame == _last_pointer_frame) {
1599 if (left_direction) {
1600 frame_delta = (_last_pointer_frame - _current_pointer_frame);
1602 frame_delta = (_current_pointer_frame - _last_pointer_frame);
1605 bool non_overlap_trim = false;
1607 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1608 non_overlap_trim = true;
1611 switch (_operation) {
1613 if ((left_direction == false) && (_current_pointer_frame <= rv->region()->first_frame()/speed)) {
1617 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1618 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1624 if ((left_direction == true) && (_current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
1628 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1629 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1636 bool swap_direction = false;
1638 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1639 swap_direction = true;
1642 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i)
1644 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1650 switch (_operation) {
1652 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1655 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1658 _editor->show_verbose_time_cursor(_current_pointer_frame, 10);
1662 _last_pointer_frame = _current_pointer_frame;
1667 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1669 if (movement_occurred) {
1670 motion (event, false);
1672 if (!_editor->selection->selected (_primary)) {
1673 _editor->thaw_region_after_trim (*_primary);
1676 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1677 _editor->thaw_region_after_trim (**i);
1678 (*i)->fake_set_opaque (true);
1682 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1684 _editor->session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1687 _editor->motion_frozen_playlists.clear ();
1689 _editor->commit_reversible_command();
1691 /* no mouse movement */
1692 _editor->point_trim (event);
1696 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1700 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1705 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1708 // create a dummy marker for visual representation of moving the copy.
1709 // The actual copying is not done before we reach the finish callback.
1711 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1712 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1713 *new MeterSection (_marker->meter()));
1715 _item = &new_marker->the_item ();
1716 _marker = new_marker;
1720 MetricSection& section (_marker->meter());
1722 if (!section.movable()) {
1728 Drag::start_grab (event, cursor);
1730 _pointer_frame_offset = _grab_frame - _marker->meter().frame();
1732 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1736 MeterMarkerDrag::motion (GdkEvent* event, bool)
1738 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1740 if (adjusted_frame == _last_pointer_frame) {
1744 _marker->set_position (adjusted_frame);
1746 _last_pointer_frame = adjusted_frame;
1748 _editor->show_verbose_time_cursor (adjusted_frame, 10);
1752 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1754 if (!movement_occurred) {
1758 motion (event, false);
1762 TempoMap& map (_editor->session->tempo_map());
1763 map.bbt_time (_last_pointer_frame, when);
1765 if (_copy == true) {
1766 _editor->begin_reversible_command (_("copy meter mark"));
1767 XMLNode &before = map.get_state();
1768 map.add_meter (_marker->meter(), when);
1769 XMLNode &after = map.get_state();
1770 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1771 _editor->commit_reversible_command ();
1773 // delete the dummy marker we used for visual representation of copying.
1774 // a new visual marker will show up automatically.
1777 _editor->begin_reversible_command (_("move meter mark"));
1778 XMLNode &before = map.get_state();
1779 map.move_meter (_marker->meter(), when);
1780 XMLNode &after = map.get_state();
1781 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1782 _editor->commit_reversible_command ();
1786 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1790 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1795 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1800 // create a dummy marker for visual representation of moving the copy.
1801 // The actual copying is not done before we reach the finish callback.
1803 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1804 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1805 *new TempoSection (_marker->tempo()));
1807 _item = &new_marker->the_item ();
1808 _marker = new_marker;
1812 MetricSection& section (_marker->tempo());
1814 if (!section.movable()) {
1819 Drag::start_grab (event, cursor);
1821 _pointer_frame_offset = _grab_frame - _marker->tempo().frame();
1822 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1826 TempoMarkerDrag::motion (GdkEvent* event, bool)
1828 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1830 if (adjusted_frame == _last_pointer_frame) {
1834 /* OK, we've moved far enough to make it worth actually move the thing. */
1836 _marker->set_position (adjusted_frame);
1838 _editor->show_verbose_time_cursor (adjusted_frame, 10);
1840 _last_pointer_frame = adjusted_frame;
1844 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1846 if (!movement_occurred) {
1850 motion (event, false);
1854 TempoMap& map (_editor->session->tempo_map());
1855 map.bbt_time (_last_pointer_frame, when);
1857 if (_copy == true) {
1858 _editor->begin_reversible_command (_("copy tempo mark"));
1859 XMLNode &before = map.get_state();
1860 map.add_tempo (_marker->tempo(), when);
1861 XMLNode &after = map.get_state();
1862 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1863 _editor->commit_reversible_command ();
1865 // delete the dummy marker we used for visual representation of copying.
1866 // a new visual marker will show up automatically.
1869 _editor->begin_reversible_command (_("move tempo mark"));
1870 XMLNode &before = map.get_state();
1871 map.move_tempo (_marker->tempo(), when);
1872 XMLNode &after = map.get_state();
1873 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1874 _editor->commit_reversible_command ();
1879 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1883 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1888 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1890 Drag::start_grab (event, c);
1894 nframes64_t where = _editor->event_frame (event, 0, 0);
1896 _editor->snap_to_with_modifier (where, event);
1897 _editor->playhead_cursor->set_position (where);
1901 if (_cursor == _editor->playhead_cursor) {
1902 _editor->_dragging_playhead = true;
1904 if (_editor->session && _was_rolling && _stop) {
1905 _editor->session->request_stop ();
1908 if (_editor->session && _editor->session->is_auditioning()) {
1909 _editor->session->cancel_audition ();
1913 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1917 CursorDrag::motion (GdkEvent* event, bool)
1919 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1921 if (adjusted_frame == _last_pointer_frame) {
1925 _cursor->set_position (adjusted_frame);
1927 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1930 _editor->update_canvas_now ();
1932 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1934 _last_pointer_frame = adjusted_frame;
1938 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1940 _editor->_dragging_playhead = false;
1942 if (!movement_occurred && _stop) {
1946 motion (event, false);
1948 if (_item == &_editor->playhead_cursor->canvas_item) {
1949 if (_editor->session) {
1950 _editor->session->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1951 _editor->_pending_locate_request = true;
1956 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1957 : RegionDrag (e, i, p, v)
1963 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1965 Drag::start_grab (event, cursor);
1967 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1968 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1970 _pointer_frame_offset = _grab_frame - ((nframes64_t) r->fade_in()->back()->when + r->position());
1974 FadeInDrag::motion (GdkEvent* event, bool)
1976 nframes64_t fade_length;
1978 nframes64_t const pos = adjusted_current_frame (event);
1980 boost::shared_ptr<Region> region = _primary->region ();
1982 if (pos < (region->position() + 64)) {
1983 fade_length = 64; // this should be a minimum defined somewhere
1984 } else if (pos > region->last_frame()) {
1985 fade_length = region->length();
1987 fade_length = pos - region->position();
1990 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1992 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1998 tmp->reset_fade_in_shape_width (fade_length);
2001 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2005 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2007 if (!movement_occurred) {
2011 nframes64_t fade_length;
2013 nframes64_t const pos = adjusted_current_frame (event);
2015 boost::shared_ptr<Region> region = _primary->region ();
2017 if (pos < (region->position() + 64)) {
2018 fade_length = 64; // this should be a minimum defined somewhere
2019 } else if (pos > region->last_frame()) {
2020 fade_length = region->length();
2022 fade_length = pos - region->position();
2025 _editor->begin_reversible_command (_("change fade in length"));
2027 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2029 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2035 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2036 XMLNode &before = alist->get_state();
2038 tmp->audio_region()->set_fade_in_length (fade_length);
2039 tmp->audio_region()->set_fade_in_active (true);
2041 XMLNode &after = alist->get_state();
2042 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2045 _editor->commit_reversible_command ();
2048 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2049 : RegionDrag (e, i, p, v)
2055 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2057 Drag::start_grab (event, cursor);
2059 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2060 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2062 _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2066 FadeOutDrag::motion (GdkEvent* event, bool)
2068 nframes64_t fade_length;
2070 nframes64_t const pos = adjusted_current_frame (event);
2072 boost::shared_ptr<Region> region = _primary->region ();
2074 if (pos > (region->last_frame() - 64)) {
2075 fade_length = 64; // this should really be a minimum fade defined somewhere
2077 else if (pos < region->position()) {
2078 fade_length = region->length();
2081 fade_length = region->last_frame() - pos;
2084 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2086 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2092 tmp->reset_fade_out_shape_width (fade_length);
2095 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2099 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2101 if (!movement_occurred) {
2105 nframes64_t fade_length;
2107 nframes64_t const pos = adjusted_current_frame (event);
2109 boost::shared_ptr<Region> region = _primary->region ();
2111 if (pos > (region->last_frame() - 64)) {
2112 fade_length = 64; // this should really be a minimum fade defined somewhere
2114 else if (pos < region->position()) {
2115 fade_length = region->length();
2118 fade_length = region->last_frame() - pos;
2121 _editor->begin_reversible_command (_("change fade out length"));
2123 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2125 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2131 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2132 XMLNode &before = alist->get_state();
2134 tmp->audio_region()->set_fade_out_length (fade_length);
2135 tmp->audio_region()->set_fade_out_active (true);
2137 XMLNode &after = alist->get_state();
2138 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2141 _editor->commit_reversible_command ();
2144 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2147 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2150 _points.push_back (Gnome::Art::Point (0, 0));
2151 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2153 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2154 _line->property_width_pixels() = 1;
2155 _line->property_points () = _points;
2158 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2161 MarkerDrag::~MarkerDrag ()
2163 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2169 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2171 Drag::start_grab (event, cursor);
2175 Location *location = _editor->find_location_from_marker (_marker, is_start);
2176 _editor->_dragging_edit_point = true;
2178 _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end());
2180 update_item (location);
2182 // _drag_line->show();
2183 // _line->raise_to_top();
2186 _editor->show_verbose_time_cursor (location->start(), 10);
2188 _editor->show_verbose_time_cursor (location->end(), 10);
2191 Selection::Operation op = Keyboard::selection_type (event->button.state);
2194 case Selection::Toggle:
2195 _editor->selection->toggle (_marker);
2197 case Selection::Set:
2198 if (!_editor->selection->selected (_marker)) {
2199 _editor->selection->set (_marker);
2202 case Selection::Extend:
2204 Locations::LocationList ll;
2205 list<Marker*> to_add;
2207 _editor->selection->markers.range (s, e);
2208 s = min (_marker->position(), s);
2209 e = max (_marker->position(), e);
2212 if (e < max_frames) {
2215 _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2216 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2217 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2220 to_add.push_back (lm->start);
2223 to_add.push_back (lm->end);
2227 if (!to_add.empty()) {
2228 _editor->selection->add (to_add);
2232 case Selection::Add:
2233 _editor->selection->add (_marker);
2237 /* set up copies for us to manipulate during the drag */
2239 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2240 Location *l = _editor->find_location_from_marker (*i, is_start);
2241 _copied_locations.push_back (new Location (*l));
2246 MarkerDrag::motion (GdkEvent* event, bool)
2248 nframes64_t f_delta = 0;
2250 bool move_both = false;
2252 Location *real_location;
2253 Location *copy_location = 0;
2255 nframes64_t const newframe = adjusted_current_frame (event);
2257 nframes64_t next = newframe;
2259 if (_current_pointer_frame == _last_pointer_frame) {
2263 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2267 MarkerSelection::iterator i;
2268 list<Location*>::iterator x;
2270 /* find the marker we're dragging, and compute the delta */
2272 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2273 x != _copied_locations.end() && i != _editor->selection->markers.end();
2279 if (marker == _marker) {
2281 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2286 if (real_location->is_mark()) {
2287 f_delta = newframe - copy_location->start();
2291 switch (marker->type()) {
2293 case Marker::LoopStart:
2294 case Marker::PunchIn:
2295 f_delta = newframe - copy_location->start();
2299 case Marker::LoopEnd:
2300 case Marker::PunchOut:
2301 f_delta = newframe - copy_location->end();
2304 /* what kind of marker is this ? */
2312 if (i == _editor->selection->markers.end()) {
2313 /* hmm, impossible - we didn't find the dragged marker */
2317 /* now move them all */
2319 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2320 x != _copied_locations.end() && i != _editor->selection->markers.end();
2326 /* call this to find out if its the start or end */
2328 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2332 if (real_location->locked()) {
2336 if (copy_location->is_mark()) {
2340 copy_location->set_start (copy_location->start() + f_delta);
2344 nframes64_t new_start = copy_location->start() + f_delta;
2345 nframes64_t new_end = copy_location->end() + f_delta;
2347 if (is_start) { // start-of-range marker
2350 copy_location->set_start (new_start);
2351 copy_location->set_end (new_end);
2352 } else if (new_start < copy_location->end()) {
2353 copy_location->set_start (new_start);
2355 _editor->snap_to (next, 1, true);
2356 copy_location->set_end (next);
2357 copy_location->set_start (newframe);
2360 } else { // end marker
2363 copy_location->set_end (new_end);
2364 copy_location->set_start (new_start);
2365 } else if (new_end > copy_location->start()) {
2366 copy_location->set_end (new_end);
2367 } else if (newframe > 0) {
2368 _editor->snap_to (next, -1, true);
2369 copy_location->set_start (next);
2370 copy_location->set_end (newframe);
2375 update_item (copy_location);
2377 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2380 lm->set_position (copy_location->start(), copy_location->end());
2384 _last_pointer_frame = _current_pointer_frame;
2386 assert (!_copied_locations.empty());
2388 _editor->edit_point_clock.set (_copied_locations.front()->start());
2389 _editor->show_verbose_time_cursor (newframe, 10);
2392 _editor->update_canvas_now ();
2394 _editor->edit_point_clock.set (copy_location->start());
2398 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2400 if (!movement_occurred) {
2402 /* just a click, do nothing but finish
2403 off the selection process
2406 Selection::Operation op = Keyboard::selection_type (event->button.state);
2409 case Selection::Set:
2410 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2411 _editor->selection->set (_marker);
2415 case Selection::Toggle:
2416 case Selection::Extend:
2417 case Selection::Add:
2424 _editor->_dragging_edit_point = false;
2426 _editor->begin_reversible_command ( _("move marker") );
2427 XMLNode &before = _editor->session->locations()->get_state();
2429 MarkerSelection::iterator i;
2430 list<Location*>::iterator x;
2433 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2434 x != _copied_locations.end() && i != _editor->selection->markers.end();
2437 Location * location = _editor->find_location_from_marker (*i, is_start);
2441 if (location->locked()) {
2445 if (location->is_mark()) {
2446 location->set_start ((*x)->start());
2448 location->set ((*x)->start(), (*x)->end());
2453 XMLNode &after = _editor->session->locations()->get_state();
2454 _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
2455 _editor->commit_reversible_command ();
2461 MarkerDrag::update_item (Location* location)
2463 double const x1 = _editor->frame_to_pixel (location->start());
2465 _points.front().set_x(x1);
2466 _points.back().set_x(x1);
2467 _line->property_points() = _points;
2470 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2472 _cumulative_x_drag (0),
2473 _cumulative_y_drag (0)
2475 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2481 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2483 Drag::start_grab (event, _editor->fader_cursor);
2485 // start the grab at the center of the control point so
2486 // the point doesn't 'jump' to the mouse after the first drag
2487 _grab_x = _point->get_x();
2488 _grab_y = _point->get_y();
2490 _point->line().parent_group().i2w (_grab_x, _grab_y);
2491 _editor->track_canvas->w2c (_grab_x, _grab_y, _grab_x, _grab_y);
2493 _grab_frame = _editor->pixel_to_frame (_grab_x);
2495 _point->line().start_drag (_point, _grab_frame, 0);
2497 float fraction = 1.0 - (_point->get_y() / _point->line().height());
2498 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2499 _current_pointer_x + 10, _current_pointer_y + 10);
2501 _editor->show_verbose_canvas_cursor ();
2505 ControlPointDrag::motion (GdkEvent* event, bool)
2507 double dx = _current_pointer_x - _last_pointer_x;
2508 double dy = _current_pointer_y - _last_pointer_y;
2510 if (event->button.state & Keyboard::SecondaryModifier) {
2515 double cx = _grab_x + _cumulative_x_drag + dx;
2516 double cy = _grab_y + _cumulative_y_drag + dy;
2518 // calculate zero crossing point. back off by .01 to stay on the
2519 // positive side of zero
2521 double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2522 _point->line().parent_group().i2w(_unused, zero_gain_y);
2524 // make sure we hit zero when passing through
2525 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2526 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2530 if (_x_constrained) {
2533 if (_y_constrained) {
2537 _cumulative_x_drag = cx - _grab_x;
2538 _cumulative_y_drag = cy - _grab_y;
2540 _point->line().parent_group().w2i (cx, cy);
2544 cy = min ((double) _point->line().height(), cy);
2546 //translate cx to frames
2547 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2549 if (!_x_constrained) {
2550 _editor->snap_to_with_modifier (cx_frames, event);
2553 float const fraction = 1.0 - (cy / _point->line().height());
2555 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2557 _point->line().point_drag (*_point, cx_frames, fraction, push);
2559 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2563 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2565 if (!movement_occurred) {
2569 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2570 _editor->reset_point_selection ();
2574 motion (event, false);
2576 _point->line().end_drag (_point);
2580 ControlPointDrag::active (Editing::MouseMode m)
2582 if (m == Editing::MouseGain) {
2583 /* always active in mouse gain */
2587 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2588 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2591 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2594 _cumulative_y_drag (0)
2599 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2601 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2604 _item = &_line->grab_item ();
2606 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2607 origin, and ditto for y.
2610 double cx = event->button.x;
2611 double cy = event->button.y;
2613 _line->parent_group().w2i (cx, cy);
2615 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2617 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
2618 /* no adjacent points */
2622 Drag::start_grab (event, _editor->fader_cursor);
2624 /* store grab start in parent frame */
2629 double fraction = 1.0 - (cy / _line->height());
2631 _line->start_drag (0, _grab_frame, fraction);
2633 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2634 _current_pointer_x + 10, _current_pointer_y + 10);
2636 _editor->show_verbose_canvas_cursor ();
2640 LineDrag::motion (GdkEvent* event, bool)
2642 double dy = _current_pointer_y - _last_pointer_y;
2644 if (event->button.state & Keyboard::SecondaryModifier) {
2648 double cy = _grab_y + _cumulative_y_drag + dy;
2650 _cumulative_y_drag = cy - _grab_y;
2653 cy = min ((double) _line->height(), cy);
2655 double const fraction = 1.0 - (cy / _line->height());
2659 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2665 _line->line_drag (_before, _after, fraction, push);
2667 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2671 LineDrag::finished (GdkEvent* event, bool)
2673 motion (event, false);
2674 _line->end_drag (0);
2678 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2680 Drag::start_grab (event);
2681 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2685 RubberbandSelectDrag::motion (GdkEvent* event, bool first_move)
2692 /* use a bigger drag threshold than the default */
2694 if (abs ((int) (_current_pointer_frame - _grab_frame)) < 8) {
2698 if (Config->get_rubberbanding_snaps_to_grid()) {
2700 _editor->snap_to_with_modifier (_grab_frame, event);
2702 _editor->snap_to_with_modifier (_current_pointer_frame, event);
2705 /* base start and end on initial click position */
2707 if (_current_pointer_frame < _grab_frame) {
2708 start = _current_pointer_frame;
2711 end = _current_pointer_frame;
2712 start = _grab_frame;
2715 if (_current_pointer_y < _grab_y) {
2716 y1 = _current_pointer_y;
2719 y2 = _current_pointer_y;
2724 if (start != end || y1 != y2) {
2726 double x1 = _editor->frame_to_pixel (start);
2727 double x2 = _editor->frame_to_pixel (end);
2729 _editor->rubberband_rect->property_x1() = x1;
2730 _editor->rubberband_rect->property_y1() = y1;
2731 _editor->rubberband_rect->property_x2() = x2;
2732 _editor->rubberband_rect->property_y2() = y2;
2734 _editor->rubberband_rect->show();
2735 _editor->rubberband_rect->raise_to_top();
2737 _last_pointer_frame = _current_pointer_frame;
2739 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2744 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2746 if (movement_occurred) {
2748 motion (event, false);
2751 if (_current_pointer_y < _grab_y) {
2752 y1 = _current_pointer_y;
2755 y2 = _current_pointer_y;
2760 Selection::Operation op = Keyboard::selection_type (event->button.state);
2763 _editor->begin_reversible_command (_("rubberband selection"));
2765 if (_grab_frame < _last_pointer_frame) {
2766 committed = _editor->select_all_within (_grab_frame, _last_pointer_frame - 1, y1, y2, _editor->track_views, op);
2768 committed = _editor->select_all_within (_last_pointer_frame, _grab_frame - 1, y1, y2, _editor->track_views, op);
2772 _editor->commit_reversible_command ();
2776 if (!getenv("ARDOUR_SAE")) {
2777 _editor->selection->clear_tracks();
2779 _editor->selection->clear_regions();
2780 _editor->selection->clear_points ();
2781 _editor->selection->clear_lines ();
2784 _editor->rubberband_rect->hide();
2788 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2790 Drag::start_grab (event);
2792 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2796 TimeFXDrag::motion (GdkEvent* event, bool)
2798 RegionView* rv = _primary;
2800 _editor->snap_to_with_modifier (_current_pointer_frame, event);
2802 if (_current_pointer_frame == _last_pointer_frame) {
2806 if (_current_pointer_frame > rv->region()->position()) {
2807 rv->get_time_axis_view().show_timestretch (rv->region()->position(), _current_pointer_frame);
2810 _last_pointer_frame = _current_pointer_frame;
2812 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2816 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2818 _primary->get_time_axis_view().hide_timestretch ();
2820 if (!movement_occurred) {
2824 if (_last_pointer_frame < _primary->region()->position()) {
2825 /* backwards drag of the left edge - not usable */
2829 nframes64_t newlen = _last_pointer_frame - _primary->region()->position();
2831 float percentage = (double) newlen / (double) _primary->region()->length();
2833 #ifndef USE_RUBBERBAND
2834 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2835 if (_primary->region()->data_type() == DataType::AUDIO) {
2836 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2840 _editor->begin_reversible_command (_("timestretch"));
2842 // XXX how do timeFX on multiple regions ?
2847 if (_editor->time_stretch (rs, percentage) == 0) {
2848 _editor->session->commit_reversible_command ();
2853 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2855 Drag::start_grab (event);
2859 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2865 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2867 if (movement_occurred && _editor->session) {
2868 /* make sure we stop */
2869 _editor->session->request_transport_speed (0.0);
2873 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2882 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2884 nframes64_t start = 0;
2885 nframes64_t end = 0;
2887 if (_editor->session == 0) {
2891 Gdk::Cursor* cursor = 0;
2893 switch (_operation) {
2894 case CreateSelection:
2895 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2900 cursor = _editor->selector_cursor;
2901 Drag::start_grab (event, cursor);
2904 case SelectionStartTrim:
2905 if (_editor->clicked_axisview) {
2906 _editor->clicked_axisview->order_selection_trims (_item, true);
2908 Drag::start_grab (event, cursor);
2909 cursor = _editor->trimmer_cursor;
2910 start = _editor->selection->time[_editor->clicked_selection].start;
2911 _pointer_frame_offset = _grab_frame - start;
2914 case SelectionEndTrim:
2915 if (_editor->clicked_axisview) {
2916 _editor->clicked_axisview->order_selection_trims (_item, false);
2918 Drag::start_grab (event, cursor);
2919 cursor = _editor->trimmer_cursor;
2920 end = _editor->selection->time[_editor->clicked_selection].end;
2921 _pointer_frame_offset = _grab_frame - end;
2925 start = _editor->selection->time[_editor->clicked_selection].start;
2926 Drag::start_grab (event, cursor);
2927 _pointer_frame_offset = _grab_frame - start;
2931 if (_operation == SelectionMove) {
2932 _editor->show_verbose_time_cursor (start, 10);
2934 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2939 SelectionDrag::motion (GdkEvent* event, bool first_move)
2941 nframes64_t start = 0;
2942 nframes64_t end = 0;
2945 nframes64_t const pending_position = adjusted_current_frame (event);
2947 /* only alter selection if the current frame is
2948 different from the last frame position (adjusted)
2951 if (pending_position == _last_pointer_frame) {
2955 switch (_operation) {
2956 case CreateSelection:
2959 _editor->snap_to (_grab_frame);
2962 if (pending_position < _grab_frame) {
2963 start = pending_position;
2966 end = pending_position;
2967 start = _grab_frame;
2970 /* first drag: Either add to the selection
2971 or create a new selection->
2976 _editor->begin_reversible_command (_("range selection"));
2979 /* adding to the selection */
2980 _editor->clicked_selection = _editor->selection->add (start, end);
2983 /* new selection-> */
2984 _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end);
2989 case SelectionStartTrim:
2992 _editor->begin_reversible_command (_("trim selection start"));
2995 start = _editor->selection->time[_editor->clicked_selection].start;
2996 end = _editor->selection->time[_editor->clicked_selection].end;
2998 if (pending_position > end) {
3001 start = pending_position;
3005 case SelectionEndTrim:
3008 _editor->begin_reversible_command (_("trim selection end"));
3011 start = _editor->selection->time[_editor->clicked_selection].start;
3012 end = _editor->selection->time[_editor->clicked_selection].end;
3014 if (pending_position < start) {
3017 end = pending_position;
3025 _editor->begin_reversible_command (_("move selection"));
3028 start = _editor->selection->time[_editor->clicked_selection].start;
3029 end = _editor->selection->time[_editor->clicked_selection].end;
3031 length = end - start;
3033 start = pending_position;
3034 _editor->snap_to (start);
3036 end = start + length;
3041 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3042 _editor->start_canvas_autoscroll (1, 0);
3046 _editor->selection->replace (_editor->clicked_selection, start, end);
3049 _last_pointer_frame = pending_position;
3051 if (_operation == SelectionMove) {
3052 _editor->show_verbose_time_cursor(start, 10);
3054 _editor->show_verbose_time_cursor(pending_position, 10);
3059 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3061 if (movement_occurred) {
3062 motion (event, false);
3063 /* XXX this is not object-oriented programming at all. ick */
3064 if (_editor->selection->time.consolidate()) {
3065 _editor->selection->TimeChanged ();
3067 _editor->commit_reversible_command ();
3069 /* just a click, no pointer movement.*/
3071 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3073 _editor->selection->clear_time();
3078 /* XXX what happens if its a music selection? */
3079 _editor->session->set_audio_range (_editor->selection->time);
3080 _editor->stop_canvas_autoscroll ();
3083 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3088 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3089 _drag_rect->hide ();
3091 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3092 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3096 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3098 if (_editor->session == 0) {
3102 Gdk::Cursor* cursor = 0;
3104 if (!_editor->temp_location) {
3105 _editor->temp_location = new Location;
3108 switch (_operation) {
3109 case CreateRangeMarker:
3110 case CreateTransportMarker:
3111 case CreateCDMarker:
3113 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3118 cursor = _editor->selector_cursor;
3122 Drag::start_grab (event, cursor);
3124 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3128 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3130 nframes64_t start = 0;
3131 nframes64_t end = 0;
3132 ArdourCanvas::SimpleRect *crect;
3134 switch (_operation) {
3135 case CreateRangeMarker:
3136 crect = _editor->range_bar_drag_rect;
3138 case CreateTransportMarker:
3139 crect = _editor->transport_bar_drag_rect;
3141 case CreateCDMarker:
3142 crect = _editor->cd_marker_bar_drag_rect;
3145 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3150 _editor->snap_to_with_modifier (_current_pointer_frame, event);
3152 /* only alter selection if the current frame is
3153 different from the last frame position.
3156 if (_current_pointer_frame == _last_pointer_frame) {
3160 switch (_operation) {
3161 case CreateRangeMarker:
3162 case CreateTransportMarker:
3163 case CreateCDMarker:
3165 _editor->snap_to (_grab_frame);
3168 if (_current_pointer_frame < _grab_frame) {
3169 start = _current_pointer_frame;
3172 end = _current_pointer_frame;
3173 start = _grab_frame;
3176 /* first drag: Either add to the selection
3177 or create a new selection.
3182 _editor->temp_location->set (start, end);
3186 update_item (_editor->temp_location);
3188 //_drag_rect->raise_to_top();
3194 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3195 _editor->start_canvas_autoscroll (1, 0);
3199 _editor->temp_location->set (start, end);
3201 double x1 = _editor->frame_to_pixel (start);
3202 double x2 = _editor->frame_to_pixel (end);
3203 crect->property_x1() = x1;
3204 crect->property_x2() = x2;
3206 update_item (_editor->temp_location);
3209 _last_pointer_frame = _current_pointer_frame;
3211 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3216 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3218 Location * newloc = 0;
3222 if (movement_occurred) {
3223 motion (event, false);
3226 switch (_operation) {
3227 case CreateRangeMarker:
3228 case CreateCDMarker:
3230 _editor->begin_reversible_command (_("new range marker"));
3231 XMLNode &before = _editor->session->locations()->get_state();
3232 _editor->session->locations()->next_available_name(rangename,"unnamed");
3233 if (_operation == CreateCDMarker) {
3234 flags = Location::IsRangeMarker | Location::IsCDMarker;
3235 _editor->cd_marker_bar_drag_rect->hide();
3238 flags = Location::IsRangeMarker;
3239 _editor->range_bar_drag_rect->hide();
3241 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3242 _editor->session->locations()->add (newloc, true);
3243 XMLNode &after = _editor->session->locations()->get_state();
3244 _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
3245 _editor->commit_reversible_command ();
3249 case CreateTransportMarker:
3250 // popup menu to pick loop or punch
3251 _editor->new_transport_marker_context_menu (&event->button, _item);
3255 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3257 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3262 start = _editor->session->locations()->first_mark_before (_grab_frame);
3263 end = _editor->session->locations()->first_mark_after (_grab_frame);
3265 if (end == max_frames) {
3266 end = _editor->session->current_end_frame ();
3270 start = _editor->session->current_start_frame ();
3273 switch (_editor->mouse_mode) {
3275 /* find the two markers on either side and then make the selection from it */
3276 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3280 /* find the two markers on either side of the click and make the range out of it */
3281 _editor->selection->set (0, start, end);
3290 _editor->stop_canvas_autoscroll ();
3296 RangeMarkerBarDrag::update_item (Location* location)
3298 double const x1 = _editor->frame_to_pixel (location->start());
3299 double const x2 = _editor->frame_to_pixel (location->end());
3301 _drag_rect->property_x1() = x1;
3302 _drag_rect->property_x2() = x2;
3306 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3308 Drag::start_grab (event, _editor->zoom_cursor);
3309 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3313 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3318 _editor->snap_to_with_modifier (_current_pointer_frame, event);
3321 _editor->snap_to_with_modifier (_grab_frame, event);
3324 if (_current_pointer_frame == _last_pointer_frame) {
3328 /* base start and end on initial click position */
3329 if (_current_pointer_frame < _grab_frame) {
3330 start = _current_pointer_frame;
3333 end = _current_pointer_frame;
3334 start = _grab_frame;
3340 _editor->zoom_rect->show();
3341 _editor->zoom_rect->raise_to_top();
3344 _editor->reposition_zoom_rect(start, end);
3346 _last_pointer_frame = _current_pointer_frame;
3348 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3353 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3355 if (movement_occurred) {
3356 motion (event, false);
3358 if (_grab_frame < _last_pointer_frame) {
3359 _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");
3361 _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom");
3364 _editor->temporal_zoom_to_frame (false, _grab_frame);
3366 temporal_zoom_step (false);
3367 center_screen (_grab_frame);
3371 _editor->zoom_rect->hide();
3374 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3377 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3378 region = &cnote->region_view();
3382 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3384 Drag::start_grab (event);
3387 drag_delta_note = 0;
3392 event_x = _current_pointer_x;
3393 event_y = _current_pointer_y;
3395 _item->property_parent().get_value()->w2i(event_x, event_y);
3397 last_x = region->snap_to_pixel(event_x);
3400 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3401 region->note_selected (cnote, true);
3405 NoteDrag::motion (GdkEvent* ev, bool)
3407 MidiStreamView* streamview = region->midi_stream_view();
3411 event_x = _current_pointer_x;
3412 event_y = _current_pointer_y;
3414 _item->property_parent().get_value()->w2i(event_x, event_y);
3416 event_x = region->snap_to_pixel(event_x);
3418 double dx = event_x - last_x;
3419 double dy = event_y - last_y;
3424 // Snap to note rows
3426 if (abs (dy) < streamview->note_height()) {
3429 int8_t this_delta_note;
3431 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3433 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3435 drag_delta_note -= this_delta_note;
3436 dy = streamview->note_height() * this_delta_note;
3437 last_y = last_y + dy;
3440 region->move_selection (dx, dy);
3444 NoteDrag::finished (GdkEvent* ev, bool moved)
3446 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3449 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3451 bool select_mod = (ev->motion.state & (Keyboard::PrimaryModifier | Keyboard::SecondaryModifier));
3453 if (cnote->selected()) {
3454 region->note_deselected (cnote, select_mod);
3456 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3457 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3459 if (!extend && !add && region->selection_size() > 1) {
3460 region->unique_select(cnote);
3462 region->note_selected (cnote, (extend ? true : add), extend);
3467 region->note_dropped (cnote, drag_delta_x, drag_delta_note);