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 && !_copy) {
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 RegionMoveDrag::x_move_allowed () const
982 if (Config->get_edit_mode() == Lock) {
984 return !_x_constrained;
986 /* in locked edit mode, reverse the usual meaning of _x_constrained */
987 return _x_constrained;
991 return !_x_constrained;
995 RegionInsertDrag::x_move_allowed () const
997 if (Config->get_edit_mode() == Lock) {
998 return _x_constrained;
1001 return !_x_constrained;
1005 RegionMotionDrag::copy_regions (GdkEvent* event)
1007 /* duplicate the regionview(s) and region(s) */
1009 list<RegionView*> new_regionviews;
1011 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1013 RegionView* rv = (*i);
1014 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1015 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1017 const boost::shared_ptr<const Region> original = rv->region();
1018 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1022 boost::shared_ptr<AudioRegion> audioregion_copy
1023 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1024 nrv = new AudioRegionView (*arv, audioregion_copy);
1026 boost::shared_ptr<MidiRegion> midiregion_copy
1027 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1028 nrv = new MidiRegionView (*mrv, midiregion_copy);
1033 nrv->get_canvas_group()->show ();
1034 new_regionviews.push_back (nrv);
1037 if (new_regionviews.empty()) {
1041 /* reflect the fact that we are dragging the copies */
1043 _primary = new_regionviews.front();
1044 _views = new_regionviews;
1046 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1049 sync the canvas to what we think is its current state
1050 without it, the canvas seems to
1051 "forget" to update properly after the upcoming reparent()
1052 ..only if the mouse is in rapid motion at the time of the grab.
1053 something to do with regionview creation raking so long?
1055 _editor->update_canvas_now();
1059 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1061 /* Which trackview is this ? */
1063 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1064 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1065 (*layer) = tvp.second;
1067 if (*tv && (*tv)->layer_display() == Overlaid) {
1071 /* The region motion is only processed if the pointer is over
1075 if (!(*tv) || !(*tv)->is_track()) {
1076 /* To make sure we hide the verbose canvas cursor when the mouse is
1077 not held over and audiotrack.
1079 _editor->hide_verbose_canvas_cursor ();
1086 /** @param new_order New track order.
1087 * @param old_order Old track order.
1088 * @param visible_y_low Lowest visible order.
1089 * @return true if y movement should not happen, otherwise false.
1092 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1094 if (new_order != old_order) {
1096 /* this isn't the pointer track */
1100 /* moving up the canvas */
1101 if ( (new_order - y_span) >= tavs.visible_y_low) {
1105 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1106 int32_t visible_tracks = 0;
1107 while (visible_tracks < y_span ) {
1109 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1110 /* passing through a hidden track */
1115 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1116 /* moving to a non-track; disallow */
1122 /* moving beyond the lowest visible track; disallow */
1126 } else if (y_span < 0) {
1128 /* moving down the canvas */
1129 if ((new_order - y_span) <= tavs.visible_y_high) {
1131 int32_t visible_tracks = 0;
1133 while (visible_tracks > y_span ) {
1136 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1137 /* passing through a hidden track */
1142 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1143 /* moving to a non-track; disallow */
1150 /* moving beyond the highest visible track; disallow */
1157 /* this is the pointer's track */
1159 if ((new_order - y_span) > tavs.visible_y_high) {
1160 /* we will overflow */
1162 } else if ((new_order - y_span) < tavs.visible_y_low) {
1163 /* we will overflow */
1172 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1173 : RegionMotionDrag (e, i, p, v, b),
1176 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1178 _dest_trackview = tv;
1179 if (tv->layer_display() == Overlaid) {
1182 _dest_layer = _primary->region()->layer ();
1186 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1187 if (rtv && rtv->is_track()) {
1188 speed = rtv->get_diskstream()->speed ();
1191 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1195 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1197 RegionMotionDrag::start_grab (event, c);
1199 _pointer_frame_offset = _grab_frame - _last_frame_position;
1202 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1203 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1205 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1206 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1208 _primary = v->view()->create_region_view (r, false, false);
1210 _primary->get_canvas_group()->show ();
1211 _primary->set_position (pos, 0);
1212 _views.push_back (_primary);
1214 _last_frame_position = pos;
1216 _item = _primary->get_canvas_group ();
1217 _dest_trackview = v;
1218 _dest_layer = _primary->region()->layer ();
1221 map<RegionView*, pair<RouteTimeAxisView*, int> >
1222 RegionMotionDrag::find_time_axis_views_and_layers ()
1224 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1226 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1228 double ix1, ix2, iy1, iy2;
1229 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1230 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1231 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1233 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1234 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1242 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1244 _editor->update_canvas_now ();
1246 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1248 RouteTimeAxisView* dest_rtv = final[_primary].first;
1250 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1251 _primary->get_canvas_group()->property_y() = 0;
1253 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1255 _editor->begin_reversible_command (_("insert region"));
1256 XMLNode& before = playlist->get_state ();
1257 playlist->add_region (_primary->region (), _last_frame_position);
1258 _editor->session->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1259 _editor->commit_reversible_command ();
1266 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1267 : RegionMoveDrag (e, i, p, v, false, false)
1272 struct RegionSelectionByPosition {
1273 bool operator() (RegionView*a, RegionView* b) {
1274 return a->region()->position () < b->region()->position();
1279 RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
1281 RouteTimeAxisView* tv;
1284 if (!check_possible (&tv, &layer)) {
1290 if (_current_pointer_x - _grab_x > 0) {
1296 RegionSelection copy (_editor->selection->regions);
1298 RegionSelectionByPosition cmp;
1301 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1303 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1309 boost::shared_ptr<Playlist> playlist;
1311 if ((playlist = atv->playlist()) == 0) {
1315 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1320 if (_current_pointer_frame < (*i)->region()->last_frame() + 1) {
1324 if (_current_pointer_frame > (*i)->region()->first_frame()) {
1330 playlist->shuffle ((*i)->region(), dir);
1332 _grab_x = _current_pointer_x;
1337 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1343 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1351 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1353 _dest_trackview = _view;
1355 Drag::start_grab (event);
1360 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1363 // TODO: create region-create-drag region view here
1366 // TODO: resize region-create-drag region view here
1370 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1372 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1378 if (!movement_occurred) {
1379 mtv->add_region (_grab_frame);
1381 motion (event, false);
1382 // TODO: create region-create-drag region here
1386 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1394 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1397 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1399 Drag::start_grab (event);
1401 region = &cnote->region_view();
1403 double region_start = region->get_position_pixels();
1404 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1406 if (_grab_x <= middle_point) {
1407 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1410 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1414 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1416 if (event->motion.state & Keyboard::PrimaryModifier) {
1422 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1424 if (ms.size() > 1) {
1425 /* has to be relative, may make no sense otherwise */
1429 region->note_selected (cnote, true);
1431 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1432 MidiRegionSelection::iterator next;
1435 (*r)->begin_resizing (at_front);
1441 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1443 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1444 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1445 (*r)->update_resizing (at_front, _current_pointer_x - _grab_x, relative);
1450 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1452 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1453 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1454 (*r)->commit_resizing (at_front, _current_pointer_x - _grab_x, relative);
1459 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1465 RegionGainDrag::finished (GdkEvent *, bool)
1470 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1471 : RegionDrag (e, i, p, v)
1477 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1480 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1481 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1483 if (tv && tv->is_track()) {
1484 speed = tv->get_diskstream()->speed();
1487 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1488 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1489 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1491 Drag::start_grab (event, _editor->trimmer_cursor);
1493 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1494 _operation = ContentsTrim;
1496 /* These will get overridden for a point trim.*/
1497 if (_current_pointer_frame < (region_start + region_length/2)) {
1498 /* closer to start */
1499 _operation = StartTrim;
1500 } else if (_current_pointer_frame > (region_end - region_length/2)) {
1502 _operation = EndTrim;
1506 switch (_operation) {
1508 _editor->show_verbose_time_cursor (region_start, 10);
1511 _editor->show_verbose_time_cursor (region_end, 10);
1514 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1520 TrimDrag::motion (GdkEvent* event, bool first_move)
1522 RegionView* rv = _primary;
1523 nframes64_t frame_delta = 0;
1525 bool left_direction;
1526 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1528 /* snap modifier works differently here..
1529 its' current state has to be passed to the
1530 various trim functions in order to work properly
1534 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1535 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1536 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1538 if (tv && tv->is_track()) {
1539 speed = tv->get_diskstream()->speed();
1542 if (_last_pointer_frame > _current_pointer_frame) {
1543 left_direction = true;
1545 left_direction = false;
1548 _editor->snap_to_with_modifier (_current_pointer_frame, event);
1554 switch (_operation) {
1556 trim_type = "Region start trim";
1559 trim_type = "Region end trim";
1562 trim_type = "Region content trim";
1566 _editor->begin_reversible_command (trim_type);
1568 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1569 (*i)->fake_set_opaque(false);
1570 (*i)->region()->freeze ();
1572 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1575 arv->temporarily_hide_envelope ();
1578 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1579 insert_result = _editor->motion_frozen_playlists.insert (pl);
1581 if (insert_result.second) {
1582 _editor->session->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1588 if (_current_pointer_frame == _last_pointer_frame) {
1592 if (left_direction) {
1593 frame_delta = (_last_pointer_frame - _current_pointer_frame);
1595 frame_delta = (_current_pointer_frame - _last_pointer_frame);
1598 bool non_overlap_trim = false;
1600 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1601 non_overlap_trim = true;
1604 switch (_operation) {
1606 if ((left_direction == false) && (_current_pointer_frame <= rv->region()->first_frame()/speed)) {
1610 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1611 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1617 if ((left_direction == true) && (_current_pointer_frame > (nframes64_t) (rv->region()->last_frame()/speed))) {
1621 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1622 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1629 bool swap_direction = false;
1631 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1632 swap_direction = true;
1635 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i)
1637 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1643 switch (_operation) {
1645 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1648 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1651 _editor->show_verbose_time_cursor(_current_pointer_frame, 10);
1655 _last_pointer_frame = _current_pointer_frame;
1660 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1662 if (movement_occurred) {
1663 motion (event, false);
1665 if (!_editor->selection->selected (_primary)) {
1666 _editor->thaw_region_after_trim (*_primary);
1669 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1670 _editor->thaw_region_after_trim (**i);
1671 (*i)->fake_set_opaque (true);
1675 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1677 _editor->session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1680 _editor->motion_frozen_playlists.clear ();
1682 _editor->commit_reversible_command();
1684 /* no mouse movement */
1685 _editor->point_trim (event);
1689 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1693 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1698 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1701 // create a dummy marker for visual representation of moving the copy.
1702 // The actual copying is not done before we reach the finish callback.
1704 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1705 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1706 *new MeterSection (_marker->meter()));
1708 _item = &new_marker->the_item ();
1709 _marker = new_marker;
1713 MetricSection& section (_marker->meter());
1715 if (!section.movable()) {
1721 Drag::start_grab (event, cursor);
1723 _pointer_frame_offset = _grab_frame - _marker->meter().frame();
1725 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1729 MeterMarkerDrag::motion (GdkEvent* event, bool)
1731 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1733 if (adjusted_frame == _last_pointer_frame) {
1737 _marker->set_position (adjusted_frame);
1739 _last_pointer_frame = adjusted_frame;
1741 _editor->show_verbose_time_cursor (adjusted_frame, 10);
1745 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1747 if (!movement_occurred) {
1751 motion (event, false);
1755 TempoMap& map (_editor->session->tempo_map());
1756 map.bbt_time (_last_pointer_frame, when);
1758 if (_copy == true) {
1759 _editor->begin_reversible_command (_("copy meter mark"));
1760 XMLNode &before = map.get_state();
1761 map.add_meter (_marker->meter(), when);
1762 XMLNode &after = map.get_state();
1763 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1764 _editor->commit_reversible_command ();
1766 // delete the dummy marker we used for visual representation of copying.
1767 // a new visual marker will show up automatically.
1770 _editor->begin_reversible_command (_("move meter mark"));
1771 XMLNode &before = map.get_state();
1772 map.move_meter (_marker->meter(), when);
1773 XMLNode &after = map.get_state();
1774 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1775 _editor->commit_reversible_command ();
1779 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1783 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1788 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1793 // create a dummy marker for visual representation of moving the copy.
1794 // The actual copying is not done before we reach the finish callback.
1796 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1797 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1798 *new TempoSection (_marker->tempo()));
1800 _item = &new_marker->the_item ();
1801 _marker = new_marker;
1805 MetricSection& section (_marker->tempo());
1807 if (!section.movable()) {
1812 Drag::start_grab (event, cursor);
1814 _pointer_frame_offset = _grab_frame - _marker->tempo().frame();
1815 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1819 TempoMarkerDrag::motion (GdkEvent* event, bool)
1821 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1823 if (adjusted_frame == _last_pointer_frame) {
1827 /* OK, we've moved far enough to make it worth actually move the thing. */
1829 _marker->set_position (adjusted_frame);
1831 _editor->show_verbose_time_cursor (adjusted_frame, 10);
1833 _last_pointer_frame = adjusted_frame;
1837 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1839 if (!movement_occurred) {
1843 motion (event, false);
1847 TempoMap& map (_editor->session->tempo_map());
1848 map.bbt_time (_last_pointer_frame, when);
1850 if (_copy == true) {
1851 _editor->begin_reversible_command (_("copy tempo mark"));
1852 XMLNode &before = map.get_state();
1853 map.add_tempo (_marker->tempo(), when);
1854 XMLNode &after = map.get_state();
1855 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1856 _editor->commit_reversible_command ();
1858 // delete the dummy marker we used for visual representation of copying.
1859 // a new visual marker will show up automatically.
1862 _editor->begin_reversible_command (_("move tempo mark"));
1863 XMLNode &before = map.get_state();
1864 map.move_tempo (_marker->tempo(), when);
1865 XMLNode &after = map.get_state();
1866 _editor->session->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1867 _editor->commit_reversible_command ();
1872 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1876 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1881 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1883 Drag::start_grab (event, c);
1887 nframes64_t where = _editor->event_frame (event, 0, 0);
1889 _editor->snap_to_with_modifier (where, event);
1890 _editor->playhead_cursor->set_position (where);
1894 if (_cursor == _editor->playhead_cursor) {
1895 _editor->_dragging_playhead = true;
1897 if (_editor->session && _was_rolling && _stop) {
1898 _editor->session->request_stop ();
1901 if (_editor->session && _editor->session->is_auditioning()) {
1902 _editor->session->cancel_audition ();
1906 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1910 CursorDrag::motion (GdkEvent* event, bool)
1912 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1914 if (adjusted_frame == _last_pointer_frame) {
1918 _cursor->set_position (adjusted_frame);
1920 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1923 _editor->update_canvas_now ();
1925 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1927 _last_pointer_frame = adjusted_frame;
1931 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1933 _editor->_dragging_playhead = false;
1935 if (!movement_occurred && _stop) {
1939 motion (event, false);
1941 if (_item == &_editor->playhead_cursor->canvas_item) {
1942 if (_editor->session) {
1943 _editor->session->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1944 _editor->_pending_locate_request = true;
1949 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1950 : RegionDrag (e, i, p, v)
1956 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1958 Drag::start_grab (event, cursor);
1960 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1961 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1963 _pointer_frame_offset = _grab_frame - ((nframes64_t) r->fade_in()->back()->when + r->position());
1967 FadeInDrag::motion (GdkEvent* event, bool)
1969 nframes64_t fade_length;
1971 nframes64_t const pos = adjusted_current_frame (event);
1973 boost::shared_ptr<Region> region = _primary->region ();
1975 if (pos < (region->position() + 64)) {
1976 fade_length = 64; // this should be a minimum defined somewhere
1977 } else if (pos > region->last_frame()) {
1978 fade_length = region->length();
1980 fade_length = pos - region->position();
1983 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1985 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1991 tmp->reset_fade_in_shape_width (fade_length);
1994 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
1998 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2000 if (!movement_occurred) {
2004 nframes64_t fade_length;
2006 nframes64_t const pos = adjusted_current_frame (event);
2008 boost::shared_ptr<Region> region = _primary->region ();
2010 if (pos < (region->position() + 64)) {
2011 fade_length = 64; // this should be a minimum defined somewhere
2012 } else if (pos > region->last_frame()) {
2013 fade_length = region->length();
2015 fade_length = pos - region->position();
2018 _editor->begin_reversible_command (_("change fade in length"));
2020 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2022 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2028 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2029 XMLNode &before = alist->get_state();
2031 tmp->audio_region()->set_fade_in_length (fade_length);
2032 tmp->audio_region()->set_fade_in_active (true);
2034 XMLNode &after = alist->get_state();
2035 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2038 _editor->commit_reversible_command ();
2041 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2042 : RegionDrag (e, i, p, v)
2048 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2050 Drag::start_grab (event, cursor);
2052 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2053 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2055 _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2059 FadeOutDrag::motion (GdkEvent* event, bool)
2061 nframes64_t fade_length;
2063 nframes64_t const pos = adjusted_current_frame (event);
2065 boost::shared_ptr<Region> region = _primary->region ();
2067 if (pos > (region->last_frame() - 64)) {
2068 fade_length = 64; // this should really be a minimum fade defined somewhere
2070 else if (pos < region->position()) {
2071 fade_length = region->length();
2074 fade_length = region->last_frame() - pos;
2077 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2079 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2085 tmp->reset_fade_out_shape_width (fade_length);
2088 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2092 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2094 if (!movement_occurred) {
2098 nframes64_t fade_length;
2100 nframes64_t const pos = adjusted_current_frame (event);
2102 boost::shared_ptr<Region> region = _primary->region ();
2104 if (pos > (region->last_frame() - 64)) {
2105 fade_length = 64; // this should really be a minimum fade defined somewhere
2107 else if (pos < region->position()) {
2108 fade_length = region->length();
2111 fade_length = region->last_frame() - pos;
2114 _editor->begin_reversible_command (_("change fade out length"));
2116 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2118 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2124 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2125 XMLNode &before = alist->get_state();
2127 tmp->audio_region()->set_fade_out_length (fade_length);
2128 tmp->audio_region()->set_fade_out_active (true);
2130 XMLNode &after = alist->get_state();
2131 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2134 _editor->commit_reversible_command ();
2137 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2140 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2143 _points.push_back (Gnome::Art::Point (0, 0));
2144 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2146 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2147 _line->property_width_pixels() = 1;
2148 _line->property_points () = _points;
2151 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2154 MarkerDrag::~MarkerDrag ()
2156 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2162 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2164 Drag::start_grab (event, cursor);
2168 Location *location = _editor->find_location_from_marker (_marker, is_start);
2169 _editor->_dragging_edit_point = true;
2171 _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end());
2173 update_item (location);
2175 // _drag_line->show();
2176 // _line->raise_to_top();
2179 _editor->show_verbose_time_cursor (location->start(), 10);
2181 _editor->show_verbose_time_cursor (location->end(), 10);
2184 Selection::Operation op = Keyboard::selection_type (event->button.state);
2187 case Selection::Toggle:
2188 _editor->selection->toggle (_marker);
2190 case Selection::Set:
2191 if (!_editor->selection->selected (_marker)) {
2192 _editor->selection->set (_marker);
2195 case Selection::Extend:
2197 Locations::LocationList ll;
2198 list<Marker*> to_add;
2200 _editor->selection->markers.range (s, e);
2201 s = min (_marker->position(), s);
2202 e = max (_marker->position(), e);
2205 if (e < max_frames) {
2208 _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2209 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2210 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2213 to_add.push_back (lm->start);
2216 to_add.push_back (lm->end);
2220 if (!to_add.empty()) {
2221 _editor->selection->add (to_add);
2225 case Selection::Add:
2226 _editor->selection->add (_marker);
2230 /* set up copies for us to manipulate during the drag */
2232 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2233 Location *l = _editor->find_location_from_marker (*i, is_start);
2234 _copied_locations.push_back (new Location (*l));
2239 MarkerDrag::motion (GdkEvent* event, bool)
2241 nframes64_t f_delta = 0;
2243 bool move_both = false;
2245 Location *real_location;
2246 Location *copy_location = 0;
2248 nframes64_t const newframe = adjusted_current_frame (event);
2250 nframes64_t next = newframe;
2252 if (_current_pointer_frame == _last_pointer_frame) {
2256 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2260 MarkerSelection::iterator i;
2261 list<Location*>::iterator x;
2263 /* find the marker we're dragging, and compute the delta */
2265 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2266 x != _copied_locations.end() && i != _editor->selection->markers.end();
2272 if (marker == _marker) {
2274 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2279 if (real_location->is_mark()) {
2280 f_delta = newframe - copy_location->start();
2284 switch (marker->type()) {
2286 case Marker::LoopStart:
2287 case Marker::PunchIn:
2288 f_delta = newframe - copy_location->start();
2292 case Marker::LoopEnd:
2293 case Marker::PunchOut:
2294 f_delta = newframe - copy_location->end();
2297 /* what kind of marker is this ? */
2305 if (i == _editor->selection->markers.end()) {
2306 /* hmm, impossible - we didn't find the dragged marker */
2310 /* now move them all */
2312 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2313 x != _copied_locations.end() && i != _editor->selection->markers.end();
2319 /* call this to find out if its the start or end */
2321 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2325 if (real_location->locked()) {
2329 if (copy_location->is_mark()) {
2333 copy_location->set_start (copy_location->start() + f_delta);
2337 nframes64_t new_start = copy_location->start() + f_delta;
2338 nframes64_t new_end = copy_location->end() + f_delta;
2340 if (is_start) { // start-of-range marker
2343 copy_location->set_start (new_start);
2344 copy_location->set_end (new_end);
2345 } else if (new_start < copy_location->end()) {
2346 copy_location->set_start (new_start);
2348 _editor->snap_to (next, 1, true);
2349 copy_location->set_end (next);
2350 copy_location->set_start (newframe);
2353 } else { // end marker
2356 copy_location->set_end (new_end);
2357 copy_location->set_start (new_start);
2358 } else if (new_end > copy_location->start()) {
2359 copy_location->set_end (new_end);
2360 } else if (newframe > 0) {
2361 _editor->snap_to (next, -1, true);
2362 copy_location->set_start (next);
2363 copy_location->set_end (newframe);
2368 update_item (copy_location);
2370 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2373 lm->set_position (copy_location->start(), copy_location->end());
2377 _last_pointer_frame = _current_pointer_frame;
2379 assert (!_copied_locations.empty());
2381 _editor->edit_point_clock.set (_copied_locations.front()->start());
2382 _editor->show_verbose_time_cursor (newframe, 10);
2385 _editor->update_canvas_now ();
2387 _editor->edit_point_clock.set (copy_location->start());
2391 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2393 if (!movement_occurred) {
2395 /* just a click, do nothing but finish
2396 off the selection process
2399 Selection::Operation op = Keyboard::selection_type (event->button.state);
2402 case Selection::Set:
2403 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2404 _editor->selection->set (_marker);
2408 case Selection::Toggle:
2409 case Selection::Extend:
2410 case Selection::Add:
2417 _editor->_dragging_edit_point = false;
2419 _editor->begin_reversible_command ( _("move marker") );
2420 XMLNode &before = _editor->session->locations()->get_state();
2422 MarkerSelection::iterator i;
2423 list<Location*>::iterator x;
2426 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2427 x != _copied_locations.end() && i != _editor->selection->markers.end();
2430 Location * location = _editor->find_location_from_marker (*i, is_start);
2434 if (location->locked()) {
2438 if (location->is_mark()) {
2439 location->set_start ((*x)->start());
2441 location->set ((*x)->start(), (*x)->end());
2446 XMLNode &after = _editor->session->locations()->get_state();
2447 _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
2448 _editor->commit_reversible_command ();
2454 MarkerDrag::update_item (Location* location)
2456 double const x1 = _editor->frame_to_pixel (location->start());
2458 _points.front().set_x(x1);
2459 _points.back().set_x(x1);
2460 _line->property_points() = _points;
2463 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2465 _cumulative_x_drag (0),
2466 _cumulative_y_drag (0)
2468 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2474 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2476 Drag::start_grab (event, _editor->fader_cursor);
2478 // start the grab at the center of the control point so
2479 // the point doesn't 'jump' to the mouse after the first drag
2480 _grab_x = _point->get_x();
2481 _grab_y = _point->get_y();
2483 _point->line().parent_group().i2w (_grab_x, _grab_y);
2484 _editor->track_canvas->w2c (_grab_x, _grab_y, _grab_x, _grab_y);
2486 _grab_frame = _editor->pixel_to_frame (_grab_x);
2488 _point->line().start_drag (_point, _grab_frame, 0);
2490 float fraction = 1.0 - (_point->get_y() / _point->line().height());
2491 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2492 _current_pointer_x + 10, _current_pointer_y + 10);
2494 _editor->show_verbose_canvas_cursor ();
2498 ControlPointDrag::motion (GdkEvent* event, bool)
2500 double dx = _current_pointer_x - _last_pointer_x;
2501 double dy = _current_pointer_y - _last_pointer_y;
2503 if (event->button.state & Keyboard::SecondaryModifier) {
2508 double cx = _grab_x + _cumulative_x_drag + dx;
2509 double cy = _grab_y + _cumulative_y_drag + dy;
2511 // calculate zero crossing point. back off by .01 to stay on the
2512 // positive side of zero
2514 double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2515 _point->line().parent_group().i2w(_unused, zero_gain_y);
2517 // make sure we hit zero when passing through
2518 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2519 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2523 if (_x_constrained) {
2526 if (_y_constrained) {
2530 _cumulative_x_drag = cx - _grab_x;
2531 _cumulative_y_drag = cy - _grab_y;
2533 _point->line().parent_group().w2i (cx, cy);
2537 cy = min ((double) _point->line().height(), cy);
2539 //translate cx to frames
2540 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2542 if (!_x_constrained) {
2543 _editor->snap_to_with_modifier (cx_frames, event);
2546 float const fraction = 1.0 - (cy / _point->line().height());
2548 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2550 _point->line().point_drag (*_point, cx_frames, fraction, push);
2552 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2556 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2558 if (!movement_occurred) {
2562 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2563 _editor->reset_point_selection ();
2567 motion (event, false);
2569 _point->line().end_drag (_point);
2573 ControlPointDrag::active (Editing::MouseMode m)
2575 if (m == Editing::MouseGain) {
2576 /* always active in mouse gain */
2580 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2581 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2584 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2587 _cumulative_y_drag (0)
2592 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2594 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2597 _item = &_line->grab_item ();
2599 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2600 origin, and ditto for y.
2603 double cx = event->button.x;
2604 double cy = event->button.y;
2606 _line->parent_group().w2i (cx, cy);
2608 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2610 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
2611 /* no adjacent points */
2615 Drag::start_grab (event, _editor->fader_cursor);
2617 /* store grab start in parent frame */
2622 double fraction = 1.0 - (cy / _line->height());
2624 _line->start_drag (0, _grab_frame, fraction);
2626 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2627 _current_pointer_x + 10, _current_pointer_y + 10);
2629 _editor->show_verbose_canvas_cursor ();
2633 LineDrag::motion (GdkEvent* event, bool)
2635 double dy = _current_pointer_y - _last_pointer_y;
2637 if (event->button.state & Keyboard::SecondaryModifier) {
2641 double cy = _grab_y + _cumulative_y_drag + dy;
2643 _cumulative_y_drag = cy - _grab_y;
2646 cy = min ((double) _line->height(), cy);
2648 double const fraction = 1.0 - (cy / _line->height());
2652 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2658 _line->line_drag (_before, _after, fraction, push);
2660 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2664 LineDrag::finished (GdkEvent* event, bool)
2666 motion (event, false);
2667 _line->end_drag (0);
2671 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2673 Drag::start_grab (event);
2674 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2678 RubberbandSelectDrag::motion (GdkEvent* event, bool first_move)
2685 /* use a bigger drag threshold than the default */
2687 if (abs ((int) (_current_pointer_frame - _grab_frame)) < 8) {
2691 if (Config->get_rubberbanding_snaps_to_grid()) {
2693 _editor->snap_to_with_modifier (_grab_frame, event);
2695 _editor->snap_to_with_modifier (_current_pointer_frame, event);
2698 /* base start and end on initial click position */
2700 if (_current_pointer_frame < _grab_frame) {
2701 start = _current_pointer_frame;
2704 end = _current_pointer_frame;
2705 start = _grab_frame;
2708 if (_current_pointer_y < _grab_y) {
2709 y1 = _current_pointer_y;
2712 y2 = _current_pointer_y;
2717 if (start != end || y1 != y2) {
2719 double x1 = _editor->frame_to_pixel (start);
2720 double x2 = _editor->frame_to_pixel (end);
2722 _editor->rubberband_rect->property_x1() = x1;
2723 _editor->rubberband_rect->property_y1() = y1;
2724 _editor->rubberband_rect->property_x2() = x2;
2725 _editor->rubberband_rect->property_y2() = y2;
2727 _editor->rubberband_rect->show();
2728 _editor->rubberband_rect->raise_to_top();
2730 _last_pointer_frame = _current_pointer_frame;
2732 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2737 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2739 if (movement_occurred) {
2741 motion (event, false);
2744 if (_current_pointer_y < _grab_y) {
2745 y1 = _current_pointer_y;
2748 y2 = _current_pointer_y;
2753 Selection::Operation op = Keyboard::selection_type (event->button.state);
2756 _editor->begin_reversible_command (_("rubberband selection"));
2758 if (_grab_frame < _last_pointer_frame) {
2759 committed = _editor->select_all_within (_grab_frame, _last_pointer_frame - 1, y1, y2, _editor->track_views, op);
2761 committed = _editor->select_all_within (_last_pointer_frame, _grab_frame - 1, y1, y2, _editor->track_views, op);
2765 _editor->commit_reversible_command ();
2769 if (!getenv("ARDOUR_SAE")) {
2770 _editor->selection->clear_tracks();
2772 _editor->selection->clear_regions();
2773 _editor->selection->clear_points ();
2774 _editor->selection->clear_lines ();
2777 _editor->rubberband_rect->hide();
2781 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2783 Drag::start_grab (event);
2785 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2789 TimeFXDrag::motion (GdkEvent* event, bool)
2791 RegionView* rv = _primary;
2793 _editor->snap_to_with_modifier (_current_pointer_frame, event);
2795 if (_current_pointer_frame == _last_pointer_frame) {
2799 if (_current_pointer_frame > rv->region()->position()) {
2800 rv->get_time_axis_view().show_timestretch (rv->region()->position(), _current_pointer_frame);
2803 _last_pointer_frame = _current_pointer_frame;
2805 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2809 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2811 _primary->get_time_axis_view().hide_timestretch ();
2813 if (!movement_occurred) {
2817 if (_last_pointer_frame < _primary->region()->position()) {
2818 /* backwards drag of the left edge - not usable */
2822 nframes64_t newlen = _last_pointer_frame - _primary->region()->position();
2824 float percentage = (double) newlen / (double) _primary->region()->length();
2826 #ifndef USE_RUBBERBAND
2827 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2828 if (_primary->region()->data_type() == DataType::AUDIO) {
2829 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2833 _editor->begin_reversible_command (_("timestretch"));
2835 // XXX how do timeFX on multiple regions ?
2840 if (_editor->time_stretch (rs, percentage) == 0) {
2841 _editor->session->commit_reversible_command ();
2846 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2848 Drag::start_grab (event);
2852 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2858 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2860 if (movement_occurred && _editor->session) {
2861 /* make sure we stop */
2862 _editor->session->request_transport_speed (0.0);
2866 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2875 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2877 nframes64_t start = 0;
2878 nframes64_t end = 0;
2880 if (_editor->session == 0) {
2884 Gdk::Cursor* cursor = 0;
2886 switch (_operation) {
2887 case CreateSelection:
2888 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2893 cursor = _editor->selector_cursor;
2894 Drag::start_grab (event, cursor);
2897 case SelectionStartTrim:
2898 if (_editor->clicked_axisview) {
2899 _editor->clicked_axisview->order_selection_trims (_item, true);
2901 Drag::start_grab (event, cursor);
2902 cursor = _editor->trimmer_cursor;
2903 start = _editor->selection->time[_editor->clicked_selection].start;
2904 _pointer_frame_offset = _grab_frame - start;
2907 case SelectionEndTrim:
2908 if (_editor->clicked_axisview) {
2909 _editor->clicked_axisview->order_selection_trims (_item, false);
2911 Drag::start_grab (event, cursor);
2912 cursor = _editor->trimmer_cursor;
2913 end = _editor->selection->time[_editor->clicked_selection].end;
2914 _pointer_frame_offset = _grab_frame - end;
2918 start = _editor->selection->time[_editor->clicked_selection].start;
2919 Drag::start_grab (event, cursor);
2920 _pointer_frame_offset = _grab_frame - start;
2924 if (_operation == SelectionMove) {
2925 _editor->show_verbose_time_cursor (start, 10);
2927 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2932 SelectionDrag::motion (GdkEvent* event, bool first_move)
2934 nframes64_t start = 0;
2935 nframes64_t end = 0;
2938 nframes64_t const pending_position = adjusted_current_frame (event);
2940 /* only alter selection if the current frame is
2941 different from the last frame position (adjusted)
2944 if (pending_position == _last_pointer_frame) {
2948 switch (_operation) {
2949 case CreateSelection:
2952 _editor->snap_to (_grab_frame);
2955 if (pending_position < _grab_frame) {
2956 start = pending_position;
2959 end = pending_position;
2960 start = _grab_frame;
2963 /* first drag: Either add to the selection
2964 or create a new selection->
2969 _editor->begin_reversible_command (_("range selection"));
2972 /* adding to the selection */
2973 _editor->clicked_selection = _editor->selection->add (start, end);
2976 /* new selection-> */
2977 _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end);
2982 case SelectionStartTrim:
2985 _editor->begin_reversible_command (_("trim selection start"));
2988 start = _editor->selection->time[_editor->clicked_selection].start;
2989 end = _editor->selection->time[_editor->clicked_selection].end;
2991 if (pending_position > end) {
2994 start = pending_position;
2998 case SelectionEndTrim:
3001 _editor->begin_reversible_command (_("trim selection end"));
3004 start = _editor->selection->time[_editor->clicked_selection].start;
3005 end = _editor->selection->time[_editor->clicked_selection].end;
3007 if (pending_position < start) {
3010 end = pending_position;
3018 _editor->begin_reversible_command (_("move selection"));
3021 start = _editor->selection->time[_editor->clicked_selection].start;
3022 end = _editor->selection->time[_editor->clicked_selection].end;
3024 length = end - start;
3026 start = pending_position;
3027 _editor->snap_to (start);
3029 end = start + length;
3034 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3035 _editor->start_canvas_autoscroll (1, 0);
3039 _editor->selection->replace (_editor->clicked_selection, start, end);
3042 _last_pointer_frame = pending_position;
3044 if (_operation == SelectionMove) {
3045 _editor->show_verbose_time_cursor(start, 10);
3047 _editor->show_verbose_time_cursor(pending_position, 10);
3052 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3054 if (movement_occurred) {
3055 motion (event, false);
3056 /* XXX this is not object-oriented programming at all. ick */
3057 if (_editor->selection->time.consolidate()) {
3058 _editor->selection->TimeChanged ();
3060 _editor->commit_reversible_command ();
3062 /* just a click, no pointer movement.*/
3064 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3066 _editor->selection->clear_time();
3071 /* XXX what happens if its a music selection? */
3072 _editor->session->set_audio_range (_editor->selection->time);
3073 _editor->stop_canvas_autoscroll ();
3076 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3081 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3082 _drag_rect->hide ();
3084 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3085 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3089 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3091 if (_editor->session == 0) {
3095 Gdk::Cursor* cursor = 0;
3097 if (!_editor->temp_location) {
3098 _editor->temp_location = new Location;
3101 switch (_operation) {
3102 case CreateRangeMarker:
3103 case CreateTransportMarker:
3104 case CreateCDMarker:
3106 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3111 cursor = _editor->selector_cursor;
3115 Drag::start_grab (event, cursor);
3117 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3121 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3123 nframes64_t start = 0;
3124 nframes64_t end = 0;
3125 ArdourCanvas::SimpleRect *crect;
3127 switch (_operation) {
3128 case CreateRangeMarker:
3129 crect = _editor->range_bar_drag_rect;
3131 case CreateTransportMarker:
3132 crect = _editor->transport_bar_drag_rect;
3134 case CreateCDMarker:
3135 crect = _editor->cd_marker_bar_drag_rect;
3138 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3143 _editor->snap_to_with_modifier (_current_pointer_frame, event);
3145 /* only alter selection if the current frame is
3146 different from the last frame position.
3149 if (_current_pointer_frame == _last_pointer_frame) {
3153 switch (_operation) {
3154 case CreateRangeMarker:
3155 case CreateTransportMarker:
3156 case CreateCDMarker:
3158 _editor->snap_to (_grab_frame);
3161 if (_current_pointer_frame < _grab_frame) {
3162 start = _current_pointer_frame;
3165 end = _current_pointer_frame;
3166 start = _grab_frame;
3169 /* first drag: Either add to the selection
3170 or create a new selection.
3175 _editor->temp_location->set (start, end);
3179 update_item (_editor->temp_location);
3181 //_drag_rect->raise_to_top();
3187 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3188 _editor->start_canvas_autoscroll (1, 0);
3192 _editor->temp_location->set (start, end);
3194 double x1 = _editor->frame_to_pixel (start);
3195 double x2 = _editor->frame_to_pixel (end);
3196 crect->property_x1() = x1;
3197 crect->property_x2() = x2;
3199 update_item (_editor->temp_location);
3202 _last_pointer_frame = _current_pointer_frame;
3204 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3209 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3211 Location * newloc = 0;
3215 if (movement_occurred) {
3216 motion (event, false);
3219 switch (_operation) {
3220 case CreateRangeMarker:
3221 case CreateCDMarker:
3223 _editor->begin_reversible_command (_("new range marker"));
3224 XMLNode &before = _editor->session->locations()->get_state();
3225 _editor->session->locations()->next_available_name(rangename,"unnamed");
3226 if (_operation == CreateCDMarker) {
3227 flags = Location::IsRangeMarker | Location::IsCDMarker;
3228 _editor->cd_marker_bar_drag_rect->hide();
3231 flags = Location::IsRangeMarker;
3232 _editor->range_bar_drag_rect->hide();
3234 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3235 _editor->session->locations()->add (newloc, true);
3236 XMLNode &after = _editor->session->locations()->get_state();
3237 _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
3238 _editor->commit_reversible_command ();
3242 case CreateTransportMarker:
3243 // popup menu to pick loop or punch
3244 _editor->new_transport_marker_context_menu (&event->button, _item);
3248 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3250 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3255 start = _editor->session->locations()->first_mark_before (_grab_frame);
3256 end = _editor->session->locations()->first_mark_after (_grab_frame);
3258 if (end == max_frames) {
3259 end = _editor->session->current_end_frame ();
3263 start = _editor->session->current_start_frame ();
3266 switch (_editor->mouse_mode) {
3268 /* find the two markers on either side and then make the selection from it */
3269 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3273 /* find the two markers on either side of the click and make the range out of it */
3274 _editor->selection->set (0, start, end);
3283 _editor->stop_canvas_autoscroll ();
3289 RangeMarkerBarDrag::update_item (Location* location)
3291 double const x1 = _editor->frame_to_pixel (location->start());
3292 double const x2 = _editor->frame_to_pixel (location->end());
3294 _drag_rect->property_x1() = x1;
3295 _drag_rect->property_x2() = x2;
3299 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3301 Drag::start_grab (event, _editor->zoom_cursor);
3302 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3306 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3311 _editor->snap_to_with_modifier (_current_pointer_frame, event);
3314 _editor->snap_to_with_modifier (_grab_frame, event);
3317 if (_current_pointer_frame == _last_pointer_frame) {
3321 /* base start and end on initial click position */
3322 if (_current_pointer_frame < _grab_frame) {
3323 start = _current_pointer_frame;
3326 end = _current_pointer_frame;
3327 start = _grab_frame;
3333 _editor->zoom_rect->show();
3334 _editor->zoom_rect->raise_to_top();
3337 _editor->reposition_zoom_rect(start, end);
3339 _last_pointer_frame = _current_pointer_frame;
3341 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3346 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3348 if (movement_occurred) {
3349 motion (event, false);
3351 if (_grab_frame < _last_pointer_frame) {
3352 _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");
3354 _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom");
3357 _editor->temporal_zoom_to_frame (false, _grab_frame);
3359 temporal_zoom_step (false);
3360 center_screen (_grab_frame);
3364 _editor->zoom_rect->hide();
3367 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3370 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3371 region = &cnote->region_view();
3375 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3377 Drag::start_grab (event);
3380 drag_delta_note = 0;
3385 event_x = _current_pointer_x;
3386 event_y = _current_pointer_y;
3388 _item->property_parent().get_value()->w2i(event_x, event_y);
3390 last_x = region->snap_to_pixel(event_x);
3393 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3395 if (!(was_selected = cnote->selected())) {
3397 /* tertiary-click means extend selection - we'll do that on button release,
3398 so don't add it here, because otherwise we make it hard to figure
3399 out the "extend-to" range.
3402 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3405 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3408 region->note_selected (cnote, true);
3410 region->unique_select (cnote);
3417 NoteDrag::motion (GdkEvent*, bool)
3419 MidiStreamView* streamview = region->midi_stream_view();
3423 event_x = _current_pointer_x;
3424 event_y = _current_pointer_y;
3426 _item->property_parent().get_value()->w2i(event_x, event_y);
3428 event_x = region->snap_to_pixel(event_x);
3430 double dx = event_x - last_x;
3431 double dy = event_y - last_y;
3436 // Snap to note rows
3438 if (abs (dy) < streamview->note_height()) {
3441 int8_t this_delta_note;
3443 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3445 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3447 drag_delta_note -= this_delta_note;
3448 dy = streamview->note_height() * this_delta_note;
3449 last_y = last_y + dy;
3453 region->move_selection (dx, dy);
3455 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3457 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3458 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3459 _editor->show_verbose_canvas_cursor_with (buf);
3464 NoteDrag::finished (GdkEvent* ev, bool moved)
3466 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3469 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3472 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3474 region->note_deselected (cnote);
3477 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3478 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3480 if (!extend && !add && region->selection_size() > 1) {
3481 region->unique_select(cnote);
3482 } else if (extend) {
3483 region->note_selected (cnote, true, true);
3485 /* it was added during button press */
3490 region->note_dropped (cnote, drag_delta_x, drag_delta_note);