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/session.h"
24 #include "ardour/dB.h"
25 #include "ardour/region_factory.h"
26 #include "ardour/midi_diskstream.h"
30 #include "audio_region_view.h"
31 #include "midi_region_view.h"
32 #include "ardour_ui.h"
33 #include "gui_thread.h"
34 #include "control_point.h"
36 #include "region_gain_line.h"
37 #include "editor_drag.h"
38 #include "audio_time_axis.h"
39 #include "midi_time_axis.h"
40 #include "canvas-note.h"
41 #include "selection.h"
42 #include "midi_selection.h"
45 using namespace ARDOUR;
48 using namespace Editing;
49 using namespace ArdourCanvas;
51 using Gtkmm2ext::Keyboard;
53 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
55 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
58 , _pointer_frame_offset (0)
59 , _have_transaction (false)
60 , _had_movement (false)
61 , _move_threshold_passed (false)
63 , _last_pointer_frame (0)
64 , _current_pointer_frame (0)
70 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
76 cursor = _editor->which_grabber_cursor ();
79 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
83 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
86 cursor = _editor->which_grabber_cursor ();
89 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
91 if (Keyboard::is_button2_event (&event->button)) {
92 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
93 _y_constrained = true;
94 _x_constrained = false;
96 _y_constrained = false;
97 _x_constrained = true;
100 _x_constrained = false;
101 _y_constrained = false;
104 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
105 _last_pointer_frame = _grab_frame;
106 _current_pointer_frame = _grab_frame;
107 _current_pointer_x = _grab_x;
108 _current_pointer_y = _grab_y;
109 _last_pointer_x = _current_pointer_x;
110 _last_pointer_y = _current_pointer_y;
114 _item->i2w (_original_x, _original_y);
116 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
120 if (_editor->session() && _editor->session()->transport_rolling()) {
123 _was_rolling = false;
126 switch (_editor->snap_type()) {
127 case SnapToRegionStart:
128 case SnapToRegionEnd:
129 case SnapToRegionSync:
130 case SnapToRegionBoundary:
131 _editor->build_region_boundary_cache ();
138 /** @param event GDK event, or 0.
139 * @return true if some movement occurred, otherwise false.
142 Drag::end_grab (GdkEvent* event)
146 _editor->stop_canvas_autoscroll ();
148 _item->ungrab (event ? event->button.time : 0);
150 _last_pointer_x = _current_pointer_x;
151 _last_pointer_y = _current_pointer_y;
152 finished (event, _had_movement);
154 _editor->hide_verbose_canvas_cursor();
158 return _had_movement;
162 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
166 if (_current_pointer_frame > _pointer_frame_offset) {
167 pos = _current_pointer_frame - _pointer_frame_offset;
171 _editor->snap_to_with_modifier (pos, event);
178 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
180 _last_pointer_x = _current_pointer_x;
181 _last_pointer_y = _current_pointer_y;
182 _last_pointer_frame = adjusted_current_frame (event);
183 _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
185 if (!from_autoscroll && !_move_threshold_passed) {
187 bool const xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
188 bool const yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
190 _move_threshold_passed = (xp || yp);
193 bool old_had_movement = _had_movement;
195 /* a motion event has happened, so we've had movement... */
196 _had_movement = true;
198 /* ... unless we're using a move threshold and we've not yet passed it */
199 if (apply_move_threshold() && !_move_threshold_passed) {
200 _had_movement = false;
203 if (active (_editor->mouse_mode) && _had_movement) {
205 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
206 if (!from_autoscroll) {
207 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
210 motion (event, _had_movement != old_had_movement);
222 _editor->stop_canvas_autoscroll ();
223 _editor->hide_verbose_canvas_cursor ();
228 /* put it back where it came from */
233 _item->i2w (cxw, cyw);
234 _item->move (_original_x - cxw, _original_y - cyw);
239 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
244 RegionView::RegionViewGoingAway.connect (death_connection, ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
248 RegionDrag::region_going_away (RegionView* v)
253 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
254 : RegionDrag (e, i, p, v),
264 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
266 Drag::start_grab (event);
268 _editor->show_verbose_time_cursor (_last_frame_position, 10);
271 RegionMotionDrag::TimeAxisViewSummary
272 RegionMotionDrag::get_time_axis_view_summary ()
274 int32_t children = 0;
275 TimeAxisViewSummary sum;
277 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
279 /* get a bitmask representing the visible tracks */
281 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
282 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
283 TimeAxisView::Children children_list;
285 /* zeroes are audio/MIDI tracks. ones are other types. */
287 if (!rtv->hidden()) {
289 if (!rtv->is_track()) {
290 /* not an audio nor MIDI track */
291 sum.tracks = sum.tracks |= (0x01 << rtv->order());
294 sum.height_list[rtv->order()] = (*i)->current_height();
297 if ((children_list = rtv->get_child_list()).size() > 0) {
298 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
299 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
300 sum.height_list[rtv->order() + children] = (*j)->current_height();
311 RegionMotionDrag::compute_y_delta (
312 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
313 int32_t last_pointer_layer, int32_t current_pointer_layer,
314 TimeAxisViewSummary const & tavs,
315 int32_t* pointer_order_span, int32_t* pointer_layer_span,
316 int32_t* canvas_pointer_order_span
320 *pointer_order_span = 0;
321 *pointer_layer_span = 0;
325 bool clamp_y_axis = false;
327 /* the change in track order between this callback and the last */
328 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
329 /* the change in layer between this callback and the last;
330 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
331 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
333 if (*pointer_order_span != 0) {
335 /* find the actual pointer span, in terms of the number of visible tracks;
336 to do this, we reduce |pointer_order_span| by the number of hidden tracks
339 *canvas_pointer_order_span = *pointer_order_span;
340 if (last_pointer_view->order() >= current_pointer_view->order()) {
341 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
342 if (tavs.height_list[y] == 0) {
343 *canvas_pointer_order_span--;
347 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
348 if (tavs.height_list[y] == 0) {
349 *canvas_pointer_order_span++;
354 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
356 RegionView* rv = (*i);
358 if (rv->region()->locked()) {
362 double ix1, ix2, iy1, iy2;
363 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
364 rv->get_canvas_frame()->i2w (ix1, iy1);
365 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
367 /* get the new trackview for this particular region */
368 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
370 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
372 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
373 as surely this is a per-region thing... */
375 clamp_y_axis = y_movement_disallowed (
376 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
384 } else if (_dest_trackview == current_pointer_view) {
386 if (current_pointer_layer == last_pointer_layer) {
387 /* No movement; clamp */
393 _dest_trackview = current_pointer_view;
394 _dest_layer = current_pointer_layer;
402 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
404 /* compute the amount of pointer motion in frames, and where
405 the region would be if we moved it by that much.
407 *pending_region_position = adjusted_current_frame (event);
409 nframes64_t sync_frame;
410 nframes64_t sync_offset;
413 sync_offset = _primary->region()->sync_offset (sync_dir);
415 /* we don't handle a sync point that lies before zero.
417 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
419 sync_frame = *pending_region_position + (sync_dir*sync_offset);
421 _editor->snap_to_with_modifier (sync_frame, event);
423 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
426 *pending_region_position = _last_frame_position;
429 if (*pending_region_position > max_frames - _primary->region()->length()) {
430 *pending_region_position = _last_frame_position;
435 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
437 /* now compute the canvas unit distance we need to move the regionview
438 to make it appear at the new location.
441 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
443 if (*pending_region_position <= _last_frame_position) {
445 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
447 RegionView* rv = (*i);
449 // If any regionview is at zero, we need to know so we can stop further leftward motion.
451 double ix1, ix2, iy1, iy2;
452 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
453 rv->get_canvas_frame()->i2w (ix1, iy1);
455 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
457 *pending_region_position = _last_frame_position;
464 _last_frame_position = *pending_region_position;
471 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
475 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
477 vector<int32_t>::iterator j;
479 /* *pointer* variables reflect things about the pointer; as we may be moving
480 multiple regions, much detail must be computed per-region */
482 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
483 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
484 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
485 is always 0 regardless of what the region's "real" layer is */
486 RouteTimeAxisView* current_pointer_view;
487 layer_t current_pointer_layer;
488 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
492 /* TimeAxisView that we were pointing at last time we entered this method */
493 TimeAxisView const * const last_pointer_view = _dest_trackview;
494 /* the order of the track that we were pointing at last time we entered this method */
495 int32_t const last_pointer_order = last_pointer_view->order ();
496 /* the layer that we were pointing at last time we entered this method */
497 layer_t const last_pointer_layer = _dest_layer;
499 int32_t pointer_order_span;
500 int32_t pointer_layer_span;
501 int32_t canvas_pointer_order_span;
503 bool const clamp_y_axis = compute_y_delta (
504 last_pointer_view, current_pointer_view,
505 last_pointer_layer, current_pointer_layer, tavs,
506 &pointer_order_span, &pointer_layer_span,
507 &canvas_pointer_order_span
510 nframes64_t pending_region_position;
511 double const x_delta = compute_x_delta (event, &pending_region_position);
513 /*************************************************************
515 ************************************************************/
517 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
518 /* haven't reached next snap point, and we're not switching
519 trackviews nor layers. nothing to do.
524 /*************************************************************
526 ************************************************************/
528 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
530 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
532 RegionView* rv = (*i);
534 if (rv->region()->locked()) {
538 /* here we are calculating the y distance from the
539 top of the first track view to the top of the region
540 area of the track view that we're working on */
542 /* this x value is just a dummy value so that we have something
547 /* distance from the top of this track view to the region area
548 of our track view is always 1 */
552 /* convert to world coordinates, ie distance from the top of
555 rv->get_canvas_frame()->i2w (ix1, iy1);
557 /* compensate for the ruler section and the vertical scrollbar position */
558 iy1 += _editor->get_trackview_group_vertical_offset ();
562 // hide any dependent views
564 rv->get_time_axis_view().hide_dependent_views (*rv);
567 reparent to a non scrolling group so that we can keep the
568 region selection above all time axis views.
569 reparenting means we have to move the rv as the two
570 parent groups have different coordinates.
573 rv->get_canvas_group()->property_y() = iy1 - 1;
574 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
576 rv->fake_set_opaque (true);
579 /* current view for this particular region */
580 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
581 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
583 if (pointer_order_span != 0 && !clamp_y_axis) {
585 /* INTER-TRACK MOVEMENT */
587 /* move through the height list to the track that the region is currently on */
588 vector<int32_t>::iterator j = tavs.height_list.begin ();
590 while (j != tavs.height_list.end () && x != rtv->order ()) {
596 int32_t temp_pointer_order_span = canvas_pointer_order_span;
598 if (j != tavs.height_list.end ()) {
600 /* Account for layers in the original and
601 destination tracks. If we're moving around in layers we assume
602 that only one track is involved, so it's ok to use *pointer*
605 StreamView* lv = last_pointer_view->view ();
608 /* move to the top of the last trackview */
609 if (lv->layer_display () == Stacked) {
610 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
613 StreamView* cv = current_pointer_view->view ();
616 /* move to the right layer on the current trackview */
617 if (cv->layer_display () == Stacked) {
618 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
621 /* And for being on a non-topmost layer on the new
624 while (temp_pointer_order_span > 0) {
625 /* we're moving up canvas-wise,
626 so we need to find the next track height
628 if (j != tavs.height_list.begin()) {
632 if (x != last_pointer_order) {
634 ++temp_pointer_order_span;
639 temp_pointer_order_span--;
642 while (temp_pointer_order_span < 0) {
646 if (x != last_pointer_order) {
648 --temp_pointer_order_span;
652 if (j != tavs.height_list.end()) {
656 temp_pointer_order_span++;
660 /* find out where we'll be when we move and set height accordingly */
662 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
663 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
664 rv->set_height (temp_rtv->view()->child_height());
666 /* if you un-comment the following, the region colours will follow
667 the track colours whilst dragging; personally
668 i think this can confuse things, but never mind.
671 //const GdkColor& col (temp_rtv->view->get_region_color());
672 //rv->set_color (const_cast<GdkColor&>(col));
676 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
678 /* INTER-LAYER MOVEMENT in the same track */
679 y_delta = rtv->view()->child_height () * pointer_layer_span;
684 _editor->mouse_brush_insert_region (rv, pending_region_position);
686 rv->move (x_delta, y_delta);
689 } /* foreach region */
692 _editor->cursor_group->raise_to_top();
695 if (x_delta != 0 && !_brushing) {
696 _editor->show_verbose_time_cursor (_last_frame_position, 10);
701 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
703 if (_copy && first_move) {
704 copy_regions (event);
707 RegionMotionDrag::motion (event, first_move);
711 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
713 vector<RegionView*> copies;
714 boost::shared_ptr<Diskstream> ds;
715 boost::shared_ptr<Playlist> from_playlist;
716 RegionSelection new_views;
717 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
718 PlaylistSet modified_playlists;
719 PlaylistSet frozen_playlists;
720 list <sigc::connection> modified_playlist_connections;
721 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
722 nframes64_t drag_delta;
723 bool changed_tracks, changed_position;
724 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
725 RouteTimeAxisView* source_tv;
727 if (!movement_occurred) {
733 /* all changes were made during motion event handlers */
736 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
737 copies.push_back (*i);
744 /* reverse this here so that we have the correct logic to finalize
748 if (Config->get_edit_mode() == Lock) {
749 _x_constrained = !_x_constrained;
753 if (_x_constrained) {
754 _editor->begin_reversible_command (_("fixed time region copy"));
756 _editor->begin_reversible_command (_("region copy"));
759 if (_x_constrained) {
760 _editor->begin_reversible_command (_("fixed time region drag"));
762 _editor->begin_reversible_command (_("region drag"));
766 _have_transaction = true;
768 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
769 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
771 drag_delta = _primary->region()->position() - _last_frame_position;
773 _editor->update_canvas_now ();
775 /* make a list of where each region ended up */
776 final = find_time_axis_views_and_layers ();
778 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
780 RegionView* rv = (*i);
781 RouteTimeAxisView* dest_rtv = final[*i].first;
782 layer_t dest_layer = final[*i].second;
786 if (rv->region()->locked()) {
791 if (changed_position && !_x_constrained) {
792 where = rv->region()->position() - drag_delta;
794 where = rv->region()->position();
797 boost::shared_ptr<Region> new_region;
800 /* we already made a copy */
801 new_region = rv->region();
803 /* undo the previous hide_dependent_views so that xfades don't
804 disappear on copying regions
807 //rv->get_time_axis_view().reveal_dependent_views (*rv);
809 } else if (changed_tracks && dest_rtv->playlist()) {
810 new_region = RegionFactory::create (rv->region());
813 if (changed_tracks || _copy) {
815 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
822 _editor->latest_regionviews.clear ();
824 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
826 insert_result = modified_playlists.insert (to_playlist);
828 if (insert_result.second) {
829 _editor->session()->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
832 to_playlist->add_region (new_region, where);
833 if (dest_rtv->view()->layer_display() == Stacked) {
834 new_region->set_layer (dest_layer);
835 new_region->set_pending_explicit_relayer (true);
840 if (!_editor->latest_regionviews.empty()) {
841 // XXX why just the first one ? we only expect one
842 // commented out in nick_m's canvas reworking. is that intended?
843 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
844 new_views.push_back (_editor->latest_regionviews.front());
849 motion on the same track. plonk the previously reparented region
850 back to its original canvas group (its streamview).
851 No need to do anything for copies as they are fake regions which will be deleted.
854 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
855 rv->get_canvas_group()->property_y() = 0;
857 /* just change the model */
859 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
861 if (dest_rtv->view()->layer_display() == Stacked) {
862 rv->region()->set_layer (dest_layer);
863 rv->region()->set_pending_explicit_relayer (true);
866 insert_result = modified_playlists.insert (playlist);
868 if (insert_result.second) {
869 _editor->session()->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
871 /* freeze to avoid lots of relayering in the case of a multi-region drag */
872 frozen_insert_result = frozen_playlists.insert(playlist);
874 if (frozen_insert_result.second) {
878 rv->region()->set_position (where, (void*) this);
881 if (changed_tracks && !_copy) {
883 /* get the playlist where this drag started. we can't use rv->region()->playlist()
884 because we may have copied the region and it has not been attached to a playlist.
887 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
888 ds = source_tv->get_diskstream();
889 from_playlist = ds->playlist();
893 assert (from_playlist);
895 /* moved to a different audio track, without copying */
897 /* the region that used to be in the old playlist is not
898 moved to the new one - we use a copy of it. as a result,
899 any existing editor for the region should no longer be
903 rv->hide_region_editor();
904 rv->fake_set_opaque (false);
906 /* remove the region from the old playlist */
908 insert_result = modified_playlists.insert (from_playlist);
910 if (insert_result.second) {
911 _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
914 from_playlist->remove_region (rv->region());
916 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
917 was selected in all of them, then removing it from a playlist will have removed all
918 trace of it from the selection (i.e. there were N regions selected, we removed 1,
919 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
920 corresponding regionview, and the selection is now empty).
922 this could have invalidated any and all iterators into the region selection.
924 the heuristic we use here is: if the region selection is empty, break out of the loop
925 here. if the region selection is not empty, then restart the loop because we know that
926 we must have removed at least the region(view) we've just been working on as well as any
927 that we processed on previous iterations.
929 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
933 if (_views.empty()) {
944 copies.push_back (rv);
948 if we've created new regions either by copying or moving
949 to a new track, we want to replace the old selection with the new ones
951 if (new_views.size() > 0) {
952 _editor->selection->set (new_views);
955 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
960 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
961 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
964 _editor->commit_reversible_command ();
966 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
973 RegionMotionDrag::x_move_allowed () const
975 if (Config->get_edit_mode() == Lock) {
976 /* in locked edit mode, reverse the usual meaning of _x_constrained */
977 return _x_constrained;
980 return !_x_constrained;
984 RegionMotionDrag::copy_regions (GdkEvent* event)
986 /* duplicate the regionview(s) and region(s) */
988 list<RegionView*> new_regionviews;
990 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
992 RegionView* rv = (*i);
993 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
994 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
996 const boost::shared_ptr<const Region> original = rv->region();
997 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1001 boost::shared_ptr<AudioRegion> audioregion_copy
1002 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1003 nrv = new AudioRegionView (*arv, audioregion_copy);
1005 boost::shared_ptr<MidiRegion> midiregion_copy
1006 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1007 nrv = new MidiRegionView (*mrv, midiregion_copy);
1012 nrv->get_canvas_group()->show ();
1013 new_regionviews.push_back (nrv);
1015 /* swap _primary to the copy */
1017 if (rv == _primary) {
1021 /* ..and deselect the one we copied */
1023 rv->set_selected (false);
1026 if (new_regionviews.empty()) {
1030 /* reflect the fact that we are dragging the copies */
1032 _views = new_regionviews;
1034 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1037 sync the canvas to what we think is its current state
1038 without it, the canvas seems to
1039 "forget" to update properly after the upcoming reparent()
1040 ..only if the mouse is in rapid motion at the time of the grab.
1041 something to do with regionview creation raking so long?
1043 _editor->update_canvas_now();
1047 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1049 /* Which trackview is this ? */
1051 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1052 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1053 (*layer) = tvp.second;
1055 if (*tv && (*tv)->layer_display() == Overlaid) {
1059 /* The region motion is only processed if the pointer is over
1063 if (!(*tv) || !(*tv)->is_track()) {
1064 /* To make sure we hide the verbose canvas cursor when the mouse is
1065 not held over and audiotrack.
1067 _editor->hide_verbose_canvas_cursor ();
1074 /** @param new_order New track order.
1075 * @param old_order Old track order.
1076 * @param visible_y_low Lowest visible order.
1077 * @return true if y movement should not happen, otherwise false.
1080 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1082 if (new_order != old_order) {
1084 /* this isn't the pointer track */
1088 /* moving up the canvas */
1089 if ( (new_order - y_span) >= tavs.visible_y_low) {
1093 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1094 int32_t visible_tracks = 0;
1095 while (visible_tracks < y_span ) {
1097 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1098 /* passing through a hidden track */
1103 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1104 /* moving to a non-track; disallow */
1110 /* moving beyond the lowest visible track; disallow */
1114 } else if (y_span < 0) {
1116 /* moving down the canvas */
1117 if ((new_order - y_span) <= tavs.visible_y_high) {
1119 int32_t visible_tracks = 0;
1121 while (visible_tracks > y_span ) {
1124 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1125 /* passing through a hidden track */
1130 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1131 /* moving to a non-track; disallow */
1138 /* moving beyond the highest visible track; disallow */
1145 /* this is the pointer's track */
1147 if ((new_order - y_span) > tavs.visible_y_high) {
1148 /* we will overflow */
1150 } else if ((new_order - y_span) < tavs.visible_y_low) {
1151 /* we will overflow */
1160 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1161 : RegionMotionDrag (e, i, p, v, b),
1164 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1166 _dest_trackview = tv;
1167 if (tv->layer_display() == Overlaid) {
1170 _dest_layer = _primary->region()->layer ();
1174 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1175 if (rtv && rtv->is_track()) {
1176 speed = rtv->get_diskstream()->speed ();
1179 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1183 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1185 RegionMotionDrag::start_grab (event, c);
1187 _pointer_frame_offset = grab_frame() - _last_frame_position;
1190 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1191 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1193 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1194 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1196 _primary = v->view()->create_region_view (r, false, false);
1198 _primary->get_canvas_group()->show ();
1199 _primary->set_position (pos, 0);
1200 _views.push_back (_primary);
1202 _last_frame_position = pos;
1204 _item = _primary->get_canvas_group ();
1205 _dest_trackview = v;
1206 _dest_layer = _primary->region()->layer ();
1209 map<RegionView*, pair<RouteTimeAxisView*, int> >
1210 RegionMotionDrag::find_time_axis_views_and_layers ()
1212 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1214 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1216 double ix1, ix2, iy1, iy2;
1217 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1218 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1219 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1221 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1222 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1230 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1232 _editor->update_canvas_now ();
1234 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1236 RouteTimeAxisView* dest_rtv = final[_primary].first;
1238 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1239 _primary->get_canvas_group()->property_y() = 0;
1241 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1243 _editor->begin_reversible_command (_("insert region"));
1244 XMLNode& before = playlist->get_state ();
1245 playlist->add_region (_primary->region (), _last_frame_position);
1246 _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1247 _editor->commit_reversible_command ();
1254 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1255 : RegionMoveDrag (e, i, p, v, false, false)
1260 struct RegionSelectionByPosition {
1261 bool operator() (RegionView*a, RegionView* b) {
1262 return a->region()->position () < b->region()->position();
1267 RegionSpliceDrag::motion (GdkEvent* event, bool)
1269 RouteTimeAxisView* tv;
1272 if (!check_possible (&tv, &layer)) {
1278 if ((current_pointer_x() - last_pointer_x()) > 0) {
1284 RegionSelection copy (_editor->selection->regions);
1286 RegionSelectionByPosition cmp;
1289 nframes64_t const pf = adjusted_current_frame (event);
1291 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1293 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1299 boost::shared_ptr<Playlist> playlist;
1301 if ((playlist = atv->playlist()) == 0) {
1305 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1310 if (pf < (*i)->region()->last_frame() + 1) {
1314 if (pf > (*i)->region()->first_frame()) {
1320 playlist->shuffle ((*i)->region(), dir);
1325 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1331 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1339 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1341 _dest_trackview = _view;
1343 Drag::start_grab (event);
1348 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1351 // TODO: create region-create-drag region view here
1354 // TODO: resize region-create-drag region view here
1358 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1360 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1366 if (!movement_occurred) {
1367 mtv->add_region (grab_frame ());
1369 motion (event, false);
1370 // TODO: create region-create-drag region here
1374 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1382 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1385 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1387 Drag::start_grab (event);
1389 region = &cnote->region_view();
1391 double region_start = region->get_position_pixels();
1392 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1394 if (grab_x() <= middle_point) {
1395 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1398 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1402 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1404 if (event->motion.state & Keyboard::PrimaryModifier) {
1410 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1412 if (ms.size() > 1) {
1413 /* has to be relative, may make no sense otherwise */
1417 region->note_selected (cnote, true);
1419 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1420 MidiRegionSelection::iterator next;
1423 (*r)->begin_resizing (at_front);
1429 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1431 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1432 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1433 (*r)->update_resizing (at_front, current_pointer_x() - grab_x(), relative);
1438 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1440 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1441 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1442 (*r)->commit_resizing (at_front, current_pointer_x() - grab_x(), relative);
1447 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1453 RegionGainDrag::finished (GdkEvent *, bool)
1458 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1459 : RegionDrag (e, i, p, v)
1465 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1468 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1469 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1471 if (tv && tv->is_track()) {
1472 speed = tv->get_diskstream()->speed();
1475 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1476 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1477 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1479 Drag::start_grab (event, _editor->trimmer_cursor);
1481 nframes64_t const pf = adjusted_current_frame (event);
1483 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1484 _operation = ContentsTrim;
1486 /* These will get overridden for a point trim.*/
1487 if (pf < (region_start + region_length/2)) {
1488 /* closer to start */
1489 _operation = StartTrim;
1490 } else if (pf > (region_end - region_length/2)) {
1492 _operation = EndTrim;
1496 switch (_operation) {
1498 _editor->show_verbose_time_cursor (region_start, 10);
1501 _editor->show_verbose_time_cursor (region_end, 10);
1504 _editor->show_verbose_time_cursor (pf, 10);
1510 TrimDrag::motion (GdkEvent* event, bool first_move)
1512 RegionView* rv = _primary;
1513 nframes64_t frame_delta = 0;
1515 bool left_direction;
1516 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1518 /* snap modifier works differently here..
1519 its current state has to be passed to the
1520 various trim functions in order to work properly
1524 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1525 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1526 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1528 if (tv && tv->is_track()) {
1529 speed = tv->get_diskstream()->speed();
1532 nframes64_t const pf = adjusted_current_frame (event);
1534 if (last_pointer_frame() > pf) {
1535 left_direction = true;
1537 left_direction = false;
1544 switch (_operation) {
1546 trim_type = "Region start trim";
1549 trim_type = "Region end trim";
1552 trim_type = "Region content trim";
1556 _editor->begin_reversible_command (trim_type);
1557 _have_transaction = true;
1559 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1560 (*i)->fake_set_opaque(false);
1561 (*i)->region()->freeze ();
1563 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1566 arv->temporarily_hide_envelope ();
1569 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1570 insert_result = _editor->motion_frozen_playlists.insert (pl);
1572 if (insert_result.second) {
1573 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1579 if (pf == last_pointer_frame()) {
1583 /* XXX i hope to god that we can really conclude this ... */
1584 _have_transaction = true;
1586 if (left_direction) {
1587 frame_delta = (last_pointer_frame() - pf);
1589 frame_delta = (pf - last_pointer_frame());
1592 bool non_overlap_trim = false;
1594 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1595 non_overlap_trim = true;
1598 switch (_operation) {
1600 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1604 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1605 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1611 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1615 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1616 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1623 bool swap_direction = false;
1625 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1626 swap_direction = true;
1629 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1630 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1636 switch (_operation) {
1638 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1641 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1644 _editor->show_verbose_time_cursor (pf, 10);
1651 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1653 if (movement_occurred) {
1654 motion (event, false);
1656 if (!_editor->selection->selected (_primary)) {
1657 _editor->thaw_region_after_trim (*_primary);
1660 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1661 _editor->thaw_region_after_trim (**i);
1662 (*i)->fake_set_opaque (true);
1665 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1667 if (_have_transaction) {
1668 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1672 _editor->motion_frozen_playlists.clear ();
1674 if (_have_transaction) {
1675 _editor->commit_reversible_command();
1679 /* no mouse movement */
1680 _editor->point_trim (event);
1684 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1688 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1693 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1696 // create a dummy marker for visual representation of moving the copy.
1697 // The actual copying is not done before we reach the finish callback.
1699 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1700 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1701 *new MeterSection (_marker->meter()));
1703 _item = &new_marker->the_item ();
1704 _marker = new_marker;
1708 MetricSection& section (_marker->meter());
1710 if (!section.movable()) {
1716 Drag::start_grab (event, cursor);
1718 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1720 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1724 MeterMarkerDrag::motion (GdkEvent* event, bool)
1726 nframes64_t const pf = adjusted_current_frame (event);
1728 if (pf == last_pointer_frame()) {
1732 _marker->set_position (pf);
1734 _editor->show_verbose_time_cursor (pf, 10);
1738 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1740 if (!movement_occurred) {
1744 motion (event, false);
1748 TempoMap& map (_editor->session()->tempo_map());
1749 map.bbt_time (last_pointer_frame(), when);
1751 if (_copy == true) {
1752 _editor->begin_reversible_command (_("copy meter mark"));
1753 XMLNode &before = map.get_state();
1754 map.add_meter (_marker->meter(), when);
1755 XMLNode &after = map.get_state();
1756 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1757 _editor->commit_reversible_command ();
1759 // delete the dummy marker we used for visual representation of copying.
1760 // a new visual marker will show up automatically.
1763 _editor->begin_reversible_command (_("move meter mark"));
1764 XMLNode &before = map.get_state();
1765 map.move_meter (_marker->meter(), when);
1766 XMLNode &after = map.get_state();
1767 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1768 _editor->commit_reversible_command ();
1772 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1776 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1781 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1786 // create a dummy marker for visual representation of moving the copy.
1787 // The actual copying is not done before we reach the finish callback.
1789 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1790 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1791 *new TempoSection (_marker->tempo()));
1793 _item = &new_marker->the_item ();
1794 _marker = new_marker;
1798 MetricSection& section (_marker->tempo());
1800 if (!section.movable()) {
1805 Drag::start_grab (event, cursor);
1807 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
1808 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1812 TempoMarkerDrag::motion (GdkEvent* event, bool)
1814 nframes64_t const pf = adjusted_current_frame (event);
1816 if (pf == last_pointer_frame()) {
1820 /* OK, we've moved far enough to make it worth actually move the thing. */
1822 _marker->set_position (pf);
1824 _editor->show_verbose_time_cursor (pf, 10);
1828 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1830 if (!movement_occurred) {
1834 motion (event, false);
1838 TempoMap& map (_editor->session()->tempo_map());
1839 map.bbt_time (last_pointer_frame(), when);
1841 if (_copy == true) {
1842 _editor->begin_reversible_command (_("copy tempo mark"));
1843 XMLNode &before = map.get_state();
1844 map.add_tempo (_marker->tempo(), when);
1845 XMLNode &after = map.get_state();
1846 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1847 _editor->commit_reversible_command ();
1849 // delete the dummy marker we used for visual representation of copying.
1850 // a new visual marker will show up automatically.
1853 _editor->begin_reversible_command (_("move tempo mark"));
1854 XMLNode &before = map.get_state();
1855 map.move_tempo (_marker->tempo(), when);
1856 XMLNode &after = map.get_state();
1857 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1858 _editor->commit_reversible_command ();
1863 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1867 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1872 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1874 Drag::start_grab (event, c);
1878 nframes64_t where = _editor->event_frame (event, 0, 0);
1880 _editor->snap_to_with_modifier (where, event);
1881 _editor->playhead_cursor->set_position (where);
1885 if (_cursor == _editor->playhead_cursor) {
1886 _editor->_dragging_playhead = true;
1888 if (_editor->session() && _was_rolling && _stop) {
1889 _editor->session()->request_stop ();
1892 if (_editor->session() && _editor->session()->is_auditioning()) {
1893 _editor->session()->cancel_audition ();
1897 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1901 CursorDrag::motion (GdkEvent* event, bool)
1903 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1905 if (adjusted_frame == last_pointer_frame()) {
1909 _cursor->set_position (adjusted_frame);
1911 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1914 _editor->update_canvas_now ();
1916 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1920 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1922 _editor->_dragging_playhead = false;
1924 if (!movement_occurred && _stop) {
1928 motion (event, false);
1930 if (_item == &_editor->playhead_cursor->canvas_item) {
1931 if (_editor->session()) {
1932 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1933 _editor->_pending_locate_request = true;
1938 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1939 : RegionDrag (e, i, p, v)
1945 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1947 Drag::start_grab (event, cursor);
1949 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1950 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1952 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
1953 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1957 FadeInDrag::motion (GdkEvent* event, bool)
1959 nframes64_t fade_length;
1961 nframes64_t const pos = adjusted_current_frame (event);
1963 boost::shared_ptr<Region> region = _primary->region ();
1965 if (pos < (region->position() + 64)) {
1966 fade_length = 64; // this should be a minimum defined somewhere
1967 } else if (pos > region->last_frame()) {
1968 fade_length = region->length();
1970 fade_length = pos - region->position();
1973 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1975 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1981 tmp->reset_fade_in_shape_width (fade_length);
1984 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
1988 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
1990 if (!movement_occurred) {
1994 nframes64_t fade_length;
1996 nframes64_t const pos = adjusted_current_frame (event);
1998 boost::shared_ptr<Region> region = _primary->region ();
2000 if (pos < (region->position() + 64)) {
2001 fade_length = 64; // this should be a minimum defined somewhere
2002 } else if (pos > region->last_frame()) {
2003 fade_length = region->length();
2005 fade_length = pos - region->position();
2008 _editor->begin_reversible_command (_("change fade in length"));
2010 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2012 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2018 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2019 XMLNode &before = alist->get_state();
2021 tmp->audio_region()->set_fade_in_length (fade_length);
2022 tmp->audio_region()->set_fade_in_active (true);
2024 XMLNode &after = alist->get_state();
2025 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2028 _editor->commit_reversible_command ();
2031 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2032 : RegionDrag (e, i, p, v)
2038 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2040 Drag::start_grab (event, cursor);
2042 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2043 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2045 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2046 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2050 FadeOutDrag::motion (GdkEvent* event, bool)
2052 nframes64_t fade_length;
2054 nframes64_t const pos = adjusted_current_frame (event);
2056 boost::shared_ptr<Region> region = _primary->region ();
2058 if (pos > (region->last_frame() - 64)) {
2059 fade_length = 64; // this should really be a minimum fade defined somewhere
2061 else if (pos < region->position()) {
2062 fade_length = region->length();
2065 fade_length = region->last_frame() - pos;
2068 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2070 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2076 tmp->reset_fade_out_shape_width (fade_length);
2079 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2083 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2085 if (!movement_occurred) {
2089 nframes64_t fade_length;
2091 nframes64_t const pos = adjusted_current_frame (event);
2093 boost::shared_ptr<Region> region = _primary->region ();
2095 if (pos > (region->last_frame() - 64)) {
2096 fade_length = 64; // this should really be a minimum fade defined somewhere
2098 else if (pos < region->position()) {
2099 fade_length = region->length();
2102 fade_length = region->last_frame() - pos;
2105 _editor->begin_reversible_command (_("change fade out length"));
2107 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2109 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2115 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2116 XMLNode &before = alist->get_state();
2118 tmp->audio_region()->set_fade_out_length (fade_length);
2119 tmp->audio_region()->set_fade_out_active (true);
2121 XMLNode &after = alist->get_state();
2122 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2125 _editor->commit_reversible_command ();
2128 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2131 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2134 _points.push_back (Gnome::Art::Point (0, 0));
2135 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2137 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2138 _line->property_width_pixels() = 1;
2139 _line->property_points () = _points;
2142 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2145 MarkerDrag::~MarkerDrag ()
2147 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2153 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2155 Drag::start_grab (event, cursor);
2159 Location *location = _editor->find_location_from_marker (_marker, is_start);
2160 _editor->_dragging_edit_point = true;
2162 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2164 update_item (location);
2166 // _drag_line->show();
2167 // _line->raise_to_top();
2170 _editor->show_verbose_time_cursor (location->start(), 10);
2172 _editor->show_verbose_time_cursor (location->end(), 10);
2175 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2178 case Selection::Toggle:
2179 _editor->selection->toggle (_marker);
2181 case Selection::Set:
2182 if (!_editor->selection->selected (_marker)) {
2183 _editor->selection->set (_marker);
2186 case Selection::Extend:
2188 Locations::LocationList ll;
2189 list<Marker*> to_add;
2191 _editor->selection->markers.range (s, e);
2192 s = min (_marker->position(), s);
2193 e = max (_marker->position(), e);
2196 if (e < max_frames) {
2199 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2200 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2201 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2204 to_add.push_back (lm->start);
2207 to_add.push_back (lm->end);
2211 if (!to_add.empty()) {
2212 _editor->selection->add (to_add);
2216 case Selection::Add:
2217 _editor->selection->add (_marker);
2221 /* set up copies for us to manipulate during the drag */
2223 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2224 Location *l = _editor->find_location_from_marker (*i, is_start);
2225 _copied_locations.push_back (new Location (*l));
2230 MarkerDrag::motion (GdkEvent* event, bool)
2232 nframes64_t f_delta = 0;
2234 bool move_both = false;
2236 Location *real_location;
2237 Location *copy_location = 0;
2239 nframes64_t const newframe = adjusted_current_frame (event);
2241 nframes64_t next = newframe;
2243 if (newframe == last_pointer_frame()) {
2247 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2251 MarkerSelection::iterator i;
2252 list<Location*>::iterator x;
2254 /* find the marker we're dragging, and compute the delta */
2256 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2257 x != _copied_locations.end() && i != _editor->selection->markers.end();
2263 if (marker == _marker) {
2265 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2270 if (real_location->is_mark()) {
2271 f_delta = newframe - copy_location->start();
2275 switch (marker->type()) {
2277 case Marker::LoopStart:
2278 case Marker::PunchIn:
2279 f_delta = newframe - copy_location->start();
2283 case Marker::LoopEnd:
2284 case Marker::PunchOut:
2285 f_delta = newframe - copy_location->end();
2288 /* what kind of marker is this ? */
2296 if (i == _editor->selection->markers.end()) {
2297 /* hmm, impossible - we didn't find the dragged marker */
2301 /* now move them all */
2303 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2304 x != _copied_locations.end() && i != _editor->selection->markers.end();
2310 /* call this to find out if its the start or end */
2312 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2316 if (real_location->locked()) {
2320 if (copy_location->is_mark()) {
2324 copy_location->set_start (copy_location->start() + f_delta);
2328 nframes64_t new_start = copy_location->start() + f_delta;
2329 nframes64_t new_end = copy_location->end() + f_delta;
2331 if (is_start) { // start-of-range marker
2334 copy_location->set_start (new_start);
2335 copy_location->set_end (new_end);
2336 } else if (new_start < copy_location->end()) {
2337 copy_location->set_start (new_start);
2339 _editor->snap_to (next, 1, true);
2340 copy_location->set_end (next);
2341 copy_location->set_start (newframe);
2344 } else { // end marker
2347 copy_location->set_end (new_end);
2348 copy_location->set_start (new_start);
2349 } else if (new_end > copy_location->start()) {
2350 copy_location->set_end (new_end);
2351 } else if (newframe > 0) {
2352 _editor->snap_to (next, -1, true);
2353 copy_location->set_start (next);
2354 copy_location->set_end (newframe);
2359 update_item (copy_location);
2361 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2364 lm->set_position (copy_location->start(), copy_location->end());
2368 assert (!_copied_locations.empty());
2370 _editor->show_verbose_time_cursor (newframe, 10);
2373 _editor->update_canvas_now ();
2378 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2380 if (!movement_occurred) {
2382 /* just a click, do nothing but finish
2383 off the selection process
2386 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2389 case Selection::Set:
2390 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2391 _editor->selection->set (_marker);
2395 case Selection::Toggle:
2396 case Selection::Extend:
2397 case Selection::Add:
2404 _editor->_dragging_edit_point = false;
2406 _editor->begin_reversible_command ( _("move marker") );
2407 XMLNode &before = _editor->session()->locations()->get_state();
2409 MarkerSelection::iterator i;
2410 list<Location*>::iterator x;
2413 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2414 x != _copied_locations.end() && i != _editor->selection->markers.end();
2417 Location * location = _editor->find_location_from_marker (*i, is_start);
2421 if (location->locked()) {
2425 if (location->is_mark()) {
2426 location->set_start ((*x)->start());
2428 location->set ((*x)->start(), (*x)->end());
2433 XMLNode &after = _editor->session()->locations()->get_state();
2434 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2435 _editor->commit_reversible_command ();
2441 MarkerDrag::update_item (Location* location)
2443 double const x1 = _editor->frame_to_pixel (location->start());
2445 _points.front().set_x(x1);
2446 _points.back().set_x(x1);
2447 _line->property_points() = _points;
2450 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2452 _cumulative_x_drag (0),
2453 _cumulative_y_drag (0)
2455 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2461 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2463 Drag::start_grab (event, _editor->fader_cursor);
2465 // start the grab at the center of the control point so
2466 // the point doesn't 'jump' to the mouse after the first drag
2467 _time_axis_view_grab_x = _point->get_x();
2468 _time_axis_view_grab_y = _point->get_y();
2470 _point->line().parent_group().i2w (_time_axis_view_grab_x, _time_axis_view_grab_y);
2471 _editor->track_canvas->w2c (_time_axis_view_grab_x, _time_axis_view_grab_y, _time_axis_view_grab_x, _time_axis_view_grab_y);
2473 _time_axis_view_grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x);
2475 _point->line().start_drag (_point, _time_axis_view_grab_frame, 0);
2477 float fraction = 1.0 - (_point->get_y() / _point->line().height());
2478 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2479 event->button.x + 10, event->button.y + 10);
2481 _editor->show_verbose_canvas_cursor ();
2485 ControlPointDrag::motion (GdkEvent* event, bool)
2487 double dx = current_pointer_x() - last_pointer_x();
2488 double dy = current_pointer_y() - last_pointer_y();
2490 if (event->button.state & Keyboard::SecondaryModifier) {
2495 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2496 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2498 // calculate zero crossing point. back off by .01 to stay on the
2499 // positive side of zero
2501 double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2502 _point->line().parent_group().i2w(_unused, zero_gain_y);
2504 // make sure we hit zero when passing through
2505 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2506 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2510 if (_x_constrained) {
2511 cx = _time_axis_view_grab_x;
2513 if (_y_constrained) {
2514 cy = _time_axis_view_grab_y;
2517 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2518 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2520 _point->line().parent_group().w2i (cx, cy);
2524 cy = min ((double) _point->line().height(), cy);
2526 //translate cx to frames
2527 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2529 if (!_x_constrained) {
2530 _editor->snap_to_with_modifier (cx_frames, event);
2533 float const fraction = 1.0 - (cy / _point->line().height());
2535 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2537 _point->line().point_drag (*_point, cx_frames, fraction, push);
2539 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2543 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2545 if (!movement_occurred) {
2549 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2550 _editor->reset_point_selection ();
2554 motion (event, false);
2556 _point->line().end_drag (_point);
2560 ControlPointDrag::active (Editing::MouseMode m)
2562 if (m == Editing::MouseGain) {
2563 /* always active in mouse gain */
2567 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2568 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2571 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2574 _cumulative_y_drag (0)
2579 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2581 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2584 _item = &_line->grab_item ();
2586 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2587 origin, and ditto for y.
2590 double cx = event->button.x;
2591 double cy = event->button.y;
2593 _line->parent_group().w2i (cx, cy);
2595 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2597 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
2598 /* no adjacent points */
2602 Drag::start_grab (event, _editor->fader_cursor);
2604 /* store grab start in parent frame */
2606 _time_axis_view_grab_x = cx;
2607 _time_axis_view_grab_y = cy;
2609 double fraction = 1.0 - (cy / _line->height());
2611 _line->start_drag (0, grab_frame(), fraction);
2613 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2614 event->button.x + 10, event->button.y + 10);
2616 _editor->show_verbose_canvas_cursor ();
2620 LineDrag::motion (GdkEvent* event, bool)
2622 double dy = current_pointer_y() - last_pointer_y();
2624 if (event->button.state & Keyboard::SecondaryModifier) {
2628 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2630 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2633 cy = min ((double) _line->height(), cy);
2635 double const fraction = 1.0 - (cy / _line->height());
2639 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2645 _line->line_drag (_before, _after, fraction, push);
2647 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2651 LineDrag::finished (GdkEvent* event, bool)
2653 motion (event, false);
2654 _line->end_drag (0);
2658 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2660 Drag::start_grab (event);
2661 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2665 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2672 /* use a bigger drag threshold than the default */
2674 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2676 if (abs ((int) (pf - grab_frame())) < 8) {
2680 nframes64_t grab = grab_frame ();
2681 if (Config->get_rubberbanding_snaps_to_grid ()) {
2682 _editor->snap_to_with_modifier (grab, event);
2685 /* base start and end on initial click position */
2695 if (current_pointer_y() < grab_y()) {
2696 y1 = current_pointer_y();
2699 y2 = current_pointer_y();
2704 if (start != end || y1 != y2) {
2706 double x1 = _editor->frame_to_pixel (start);
2707 double x2 = _editor->frame_to_pixel (end);
2709 _editor->rubberband_rect->property_x1() = x1;
2710 _editor->rubberband_rect->property_y1() = y1;
2711 _editor->rubberband_rect->property_x2() = x2;
2712 _editor->rubberband_rect->property_y2() = y2;
2714 _editor->rubberband_rect->show();
2715 _editor->rubberband_rect->raise_to_top();
2717 _editor->show_verbose_time_cursor (pf, 10);
2722 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2724 if (movement_occurred) {
2726 motion (event, false);
2729 if (current_pointer_y() < grab_y()) {
2730 y1 = current_pointer_y();
2733 y2 = current_pointer_y();
2738 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2741 _editor->begin_reversible_command (_("rubberband selection"));
2743 if (grab_frame() < last_pointer_frame()) {
2744 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
2746 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
2750 _editor->commit_reversible_command ();
2754 if (!getenv("ARDOUR_SAE")) {
2755 _editor->selection->clear_tracks();
2757 _editor->selection->clear_regions();
2758 _editor->selection->clear_points ();
2759 _editor->selection->clear_lines ();
2762 _editor->rubberband_rect->hide();
2766 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2768 Drag::start_grab (event);
2770 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2774 TimeFXDrag::motion (GdkEvent* event, bool)
2776 RegionView* rv = _primary;
2778 nframes64_t const pf = adjusted_current_frame (event);
2780 if (pf == last_pointer_frame()) {
2784 if (pf > rv->region()->position()) {
2785 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2788 _editor->show_verbose_time_cursor (pf, 10);
2792 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2794 _primary->get_time_axis_view().hide_timestretch ();
2796 if (!movement_occurred) {
2800 if (last_pointer_frame() < _primary->region()->position()) {
2801 /* backwards drag of the left edge - not usable */
2805 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2807 float percentage = (double) newlen / (double) _primary->region()->length();
2809 #ifndef USE_RUBBERBAND
2810 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2811 if (_primary->region()->data_type() == DataType::AUDIO) {
2812 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2816 _editor->begin_reversible_command (_("timestretch"));
2818 // XXX how do timeFX on multiple regions ?
2823 if (!_editor->time_stretch (rs, percentage) == 0) {
2824 error << _("An error occurred while executing time stretch operation") << endmsg;
2829 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2831 Drag::start_grab (event);
2835 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2841 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2843 if (movement_occurred && _editor->session()) {
2844 /* make sure we stop */
2845 _editor->session()->request_transport_speed (0.0);
2849 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2858 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2860 nframes64_t start = 0;
2861 nframes64_t end = 0;
2863 if (_editor->session() == 0) {
2867 Gdk::Cursor* cursor = 0;
2869 switch (_operation) {
2870 case CreateSelection:
2871 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2876 cursor = _editor->selector_cursor;
2877 Drag::start_grab (event, cursor);
2880 case SelectionStartTrim:
2881 if (_editor->clicked_axisview) {
2882 _editor->clicked_axisview->order_selection_trims (_item, true);
2884 Drag::start_grab (event, cursor);
2885 cursor = _editor->trimmer_cursor;
2886 start = _editor->selection->time[_editor->clicked_selection].start;
2887 _pointer_frame_offset = grab_frame() - start;
2890 case SelectionEndTrim:
2891 if (_editor->clicked_axisview) {
2892 _editor->clicked_axisview->order_selection_trims (_item, false);
2894 Drag::start_grab (event, cursor);
2895 cursor = _editor->trimmer_cursor;
2896 end = _editor->selection->time[_editor->clicked_selection].end;
2897 _pointer_frame_offset = grab_frame() - end;
2901 start = _editor->selection->time[_editor->clicked_selection].start;
2902 Drag::start_grab (event, cursor);
2903 _pointer_frame_offset = grab_frame() - start;
2907 if (_operation == SelectionMove) {
2908 _editor->show_verbose_time_cursor (start, 10);
2910 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2915 SelectionDrag::motion (GdkEvent* event, bool first_move)
2917 nframes64_t start = 0;
2918 nframes64_t end = 0;
2921 nframes64_t const pending_position = adjusted_current_frame (event);
2923 /* only alter selection if the current frame is
2924 different from the last frame position (adjusted)
2927 if (pending_position == last_pointer_frame()) {
2931 switch (_operation) {
2932 case CreateSelection:
2934 nframes64_t grab = grab_frame ();
2937 _editor->snap_to (grab);
2940 if (pending_position < grab_frame()) {
2941 start = pending_position;
2944 end = pending_position;
2948 /* first drag: Either add to the selection
2949 or create a new selection
2954 _editor->begin_reversible_command (_("range selection"));
2955 _have_transaction = true;
2958 /* adding to the selection */
2959 _editor->selection->add (_editor->clicked_axisview);
2960 _editor->clicked_selection = _editor->selection->add (start, end);
2965 if (!_editor->selection->selected (_editor->clicked_axisview)) {
2966 _editor->selection->set (_editor->clicked_axisview);
2969 _editor->clicked_selection = _editor->selection->set (start, end);
2975 case SelectionStartTrim:
2978 _editor->begin_reversible_command (_("trim selection start"));
2979 _have_transaction = true;
2982 start = _editor->selection->time[_editor->clicked_selection].start;
2983 end = _editor->selection->time[_editor->clicked_selection].end;
2985 if (pending_position > end) {
2988 start = pending_position;
2992 case SelectionEndTrim:
2995 _editor->begin_reversible_command (_("trim selection end"));
2996 _have_transaction = true;
2999 start = _editor->selection->time[_editor->clicked_selection].start;
3000 end = _editor->selection->time[_editor->clicked_selection].end;
3002 if (pending_position < start) {
3005 end = pending_position;
3013 _editor->begin_reversible_command (_("move selection"));
3014 _have_transaction = true;
3017 start = _editor->selection->time[_editor->clicked_selection].start;
3018 end = _editor->selection->time[_editor->clicked_selection].end;
3020 length = end - start;
3022 start = pending_position;
3023 _editor->snap_to (start);
3025 end = start + length;
3030 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3031 _editor->start_canvas_autoscroll (1, 0);
3035 _editor->selection->replace (_editor->clicked_selection, start, end);
3038 if (_operation == SelectionMove) {
3039 _editor->show_verbose_time_cursor(start, 10);
3041 _editor->show_verbose_time_cursor(pending_position, 10);
3046 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3048 Session* s = _editor->session();
3050 if (movement_occurred) {
3051 motion (event, false);
3052 /* XXX this is not object-oriented programming at all. ick */
3053 if (_editor->selection->time.consolidate()) {
3054 _editor->selection->TimeChanged ();
3057 if (_have_transaction) {
3058 _editor->commit_reversible_command ();
3061 /* XXX what if its a music time selection? */
3062 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3063 s->request_play_range (&_editor->selection->time, true);
3068 /* just a click, no pointer movement.*/
3070 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3071 _editor->selection->clear_time();
3074 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3075 _editor->selection->set (_editor->clicked_axisview);
3078 if (s && s->get_play_range () && s->transport_rolling()) {
3079 s->request_stop (false, false);
3084 _editor->stop_canvas_autoscroll ();
3087 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3092 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3093 _drag_rect->hide ();
3095 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3096 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3100 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3102 if (_editor->session() == 0) {
3106 Gdk::Cursor* cursor = 0;
3108 if (!_editor->temp_location) {
3109 _editor->temp_location = new Location;
3112 switch (_operation) {
3113 case CreateRangeMarker:
3114 case CreateTransportMarker:
3115 case CreateCDMarker:
3117 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3122 cursor = _editor->selector_cursor;
3126 Drag::start_grab (event, cursor);
3128 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3132 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3134 nframes64_t start = 0;
3135 nframes64_t end = 0;
3136 ArdourCanvas::SimpleRect *crect;
3138 switch (_operation) {
3139 case CreateRangeMarker:
3140 crect = _editor->range_bar_drag_rect;
3142 case CreateTransportMarker:
3143 crect = _editor->transport_bar_drag_rect;
3145 case CreateCDMarker:
3146 crect = _editor->cd_marker_bar_drag_rect;
3149 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3154 nframes64_t const pf = adjusted_current_frame (event);
3156 /* only alter selection if the current frame is
3157 different from the last frame position.
3160 if (pf == last_pointer_frame()) {
3164 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3165 nframes64_t grab = grab_frame ();
3166 _editor->snap_to (grab);
3168 if (pf < 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();
3193 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3194 _editor->start_canvas_autoscroll (1, 0);
3198 _editor->temp_location->set (start, end);
3200 double x1 = _editor->frame_to_pixel (start);
3201 double x2 = _editor->frame_to_pixel (end);
3202 crect->property_x1() = x1;
3203 crect->property_x2() = x2;
3205 update_item (_editor->temp_location);
3208 _editor->show_verbose_time_cursor (pf, 10);
3213 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3215 Location * newloc = 0;
3219 if (movement_occurred) {
3220 motion (event, false);
3223 switch (_operation) {
3224 case CreateRangeMarker:
3225 case CreateCDMarker:
3227 _editor->begin_reversible_command (_("new range marker"));
3228 XMLNode &before = _editor->session()->locations()->get_state();
3229 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3230 if (_operation == CreateCDMarker) {
3231 flags = Location::IsRangeMarker | Location::IsCDMarker;
3232 _editor->cd_marker_bar_drag_rect->hide();
3235 flags = Location::IsRangeMarker;
3236 _editor->range_bar_drag_rect->hide();
3238 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3239 _editor->session()->locations()->add (newloc, true);
3240 XMLNode &after = _editor->session()->locations()->get_state();
3241 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3242 _editor->commit_reversible_command ();
3246 case CreateTransportMarker:
3247 // popup menu to pick loop or punch
3248 _editor->new_transport_marker_context_menu (&event->button, _item);
3252 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3254 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3259 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3261 if (end == max_frames) {
3262 end = _editor->session()->current_end_frame ();
3265 if (start == max_frames) {
3266 start = _editor->session()->current_start_frame ();
3269 switch (_editor->mouse_mode) {
3271 /* find the two markers on either side and then make the selection from it */
3272 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3276 /* find the two markers on either side of the click and make the range out of it */
3277 _editor->selection->set (start, end);
3286 _editor->stop_canvas_autoscroll ();
3292 RangeMarkerBarDrag::update_item (Location* location)
3294 double const x1 = _editor->frame_to_pixel (location->start());
3295 double const x2 = _editor->frame_to_pixel (location->end());
3297 _drag_rect->property_x1() = x1;
3298 _drag_rect->property_x2() = x2;
3302 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3304 Drag::start_grab (event, _editor->zoom_cursor);
3305 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3309 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3314 nframes64_t const pf = adjusted_current_frame (event);
3316 if (pf == last_pointer_frame()) {
3320 nframes64_t grab = grab_frame ();
3321 _editor->snap_to_with_modifier (grab, event);
3323 /* base start and end on initial click position */
3335 _editor->zoom_rect->show();
3336 _editor->zoom_rect->raise_to_top();
3339 _editor->reposition_zoom_rect(start, end);
3341 _editor->show_verbose_time_cursor (pf, 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);