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"
43 #include "automation_time_axis.h"
46 using namespace ARDOUR;
49 using namespace Editing;
50 using namespace ArdourCanvas;
52 using Gtkmm2ext::Keyboard;
54 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
56 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
59 , _pointer_frame_offset (0)
60 , _have_transaction (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 _grab_frame = adjusted_frame (_grab_frame, event);
106 _last_pointer_frame = _grab_frame;
107 _current_pointer_frame = _grab_frame;
108 _current_pointer_x = _grab_x;
109 _current_pointer_y = _grab_y;
110 _last_pointer_x = _current_pointer_x;
111 _last_pointer_y = _current_pointer_y;
115 _item->i2w (_original_x, _original_y);
117 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
121 if (_editor->session() && _editor->session()->transport_rolling()) {
124 _was_rolling = false;
127 switch (_editor->snap_type()) {
128 case SnapToRegionStart:
129 case SnapToRegionEnd:
130 case SnapToRegionSync:
131 case SnapToRegionBoundary:
132 _editor->build_region_boundary_cache ();
139 /** @param event GDK event, or 0.
140 * @return true if some movement occurred, otherwise false.
143 Drag::end_grab (GdkEvent* event)
147 _editor->stop_canvas_autoscroll ();
149 _item->ungrab (event ? event->button.time : 0);
151 _last_pointer_x = _current_pointer_x;
152 _last_pointer_y = _current_pointer_y;
153 finished (event, _move_threshold_passed);
155 _editor->hide_verbose_canvas_cursor();
159 return _move_threshold_passed;
163 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
167 if (f > _pointer_frame_offset) {
168 pos = f - _pointer_frame_offset;
172 _editor->snap_to_with_modifier (pos, event);
179 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
181 return adjusted_frame (_current_pointer_frame, event, snap);
185 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
187 _last_pointer_x = _current_pointer_x;
188 _last_pointer_y = _current_pointer_y;
189 _last_pointer_frame = adjusted_current_frame (event);
190 _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
192 /* check to see if we have moved in any way that matters since the last motion event */
193 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
194 (!y_movement_matters() || _last_pointer_y == _current_pointer_y) ) {
198 pair<nframes64_t, int> const threshold = move_threshold ();
200 bool const old_move_threshold_passed = _move_threshold_passed;
202 if (!from_autoscroll && !_move_threshold_passed) {
204 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
205 bool const yp = (::fabs ((_current_pointer_y - _grab_y)) >= threshold.second);
207 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
210 if (active (_editor->mouse_mode) && _move_threshold_passed) {
212 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
213 if (!from_autoscroll) {
214 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
217 motion (event, _move_threshold_passed != old_move_threshold_passed);
229 _editor->stop_canvas_autoscroll ();
230 _editor->hide_verbose_canvas_cursor ();
235 /* put it back where it came from */
240 _item->i2w (cxw, cyw);
241 _item->move (_original_x - cxw, _original_y - cyw);
246 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
251 RegionView::RegionViewGoingAway.connect (death_connection, ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
255 RegionDrag::region_going_away (RegionView* v)
260 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
261 : RegionDrag (e, i, p, v),
271 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
273 Drag::start_grab (event);
275 _editor->show_verbose_time_cursor (_last_frame_position, 10);
278 RegionMotionDrag::TimeAxisViewSummary
279 RegionMotionDrag::get_time_axis_view_summary ()
281 int32_t children = 0;
282 TimeAxisViewSummary sum;
284 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
286 /* get a bitmask representing the visible tracks */
288 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
289 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
290 TimeAxisView::Children children_list;
292 /* zeroes are audio/MIDI tracks. ones are other types. */
294 if (!rtv->hidden()) {
296 if (!rtv->is_track()) {
297 /* not an audio nor MIDI track */
298 sum.tracks = sum.tracks |= (0x01 << rtv->order());
301 sum.height_list[rtv->order()] = (*i)->current_height();
304 if ((children_list = rtv->get_child_list()).size() > 0) {
305 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
306 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
307 sum.height_list[rtv->order() + children] = (*j)->current_height();
318 RegionMotionDrag::compute_y_delta (
319 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
320 int32_t last_pointer_layer, int32_t current_pointer_layer,
321 TimeAxisViewSummary const & tavs,
322 int32_t* pointer_order_span, int32_t* pointer_layer_span,
323 int32_t* canvas_pointer_order_span
327 *pointer_order_span = 0;
328 *pointer_layer_span = 0;
332 bool clamp_y_axis = false;
334 /* the change in track order between this callback and the last */
335 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
336 /* the change in layer between this callback and the last;
337 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
338 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
340 if (*pointer_order_span != 0) {
342 /* find the actual pointer span, in terms of the number of visible tracks;
343 to do this, we reduce |pointer_order_span| by the number of hidden tracks
346 *canvas_pointer_order_span = *pointer_order_span;
347 if (last_pointer_view->order() >= current_pointer_view->order()) {
348 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
349 if (tavs.height_list[y] == 0) {
350 *canvas_pointer_order_span--;
354 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
355 if (tavs.height_list[y] == 0) {
356 *canvas_pointer_order_span++;
361 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
363 RegionView* rv = (*i);
365 if (rv->region()->locked()) {
369 double ix1, ix2, iy1, iy2;
370 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
371 rv->get_canvas_frame()->i2w (ix1, iy1);
372 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
374 /* get the new trackview for this particular region */
375 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
377 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
379 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
380 as surely this is a per-region thing... */
382 clamp_y_axis = y_movement_disallowed (
383 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
391 } else if (_dest_trackview == current_pointer_view) {
393 if (current_pointer_layer == last_pointer_layer) {
394 /* No movement; clamp */
400 _dest_trackview = current_pointer_view;
401 _dest_layer = current_pointer_layer;
409 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
411 /* compute the amount of pointer motion in frames, and where
412 the region would be if we moved it by that much.
414 *pending_region_position = adjusted_current_frame (event);
416 nframes64_t sync_frame;
417 nframes64_t sync_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;
436 if (*pending_region_position > max_frames - _primary->region()->length()) {
437 *pending_region_position = _last_frame_position;
442 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
444 /* now compute the canvas unit distance we need to move the regionview
445 to make it appear at the new location.
448 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
450 if (*pending_region_position <= _last_frame_position) {
452 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
454 RegionView* rv = (*i);
456 // If any regionview is at zero, we need to know so we can stop further leftward motion.
458 double ix1, ix2, iy1, iy2;
459 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
460 rv->get_canvas_frame()->i2w (ix1, iy1);
462 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
464 *pending_region_position = _last_frame_position;
471 _last_frame_position = *pending_region_position;
478 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
482 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
484 vector<int32_t>::iterator j;
486 /* *pointer* variables reflect things about the pointer; as we may be moving
487 multiple regions, much detail must be computed per-region */
489 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
490 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
491 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
492 is always 0 regardless of what the region's "real" layer is */
493 RouteTimeAxisView* current_pointer_view;
494 layer_t current_pointer_layer;
495 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
499 /* TimeAxisView that we were pointing at last time we entered this method */
500 TimeAxisView const * const last_pointer_view = _dest_trackview;
501 /* the order of the track that we were pointing at last time we entered this method */
502 int32_t const last_pointer_order = last_pointer_view->order ();
503 /* the layer that we were pointing at last time we entered this method */
504 layer_t const last_pointer_layer = _dest_layer;
506 int32_t pointer_order_span;
507 int32_t pointer_layer_span;
508 int32_t canvas_pointer_order_span;
510 bool const clamp_y_axis = compute_y_delta (
511 last_pointer_view, current_pointer_view,
512 last_pointer_layer, current_pointer_layer, tavs,
513 &pointer_order_span, &pointer_layer_span,
514 &canvas_pointer_order_span
517 nframes64_t pending_region_position;
518 double const x_delta = compute_x_delta (event, &pending_region_position);
520 /*************************************************************
522 ************************************************************/
524 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
525 /* haven't reached next snap point, and we're not switching
526 trackviews nor layers. nothing to do.
531 /*************************************************************
533 ************************************************************/
535 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
537 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
539 RegionView* rv = (*i);
541 if (rv->region()->locked()) {
545 /* here we are calculating the y distance from the
546 top of the first track view to the top of the region
547 area of the track view that we're working on */
549 /* this x value is just a dummy value so that we have something
554 /* distance from the top of this track view to the region area
555 of our track view is always 1 */
559 /* convert to world coordinates, ie distance from the top of
562 rv->get_canvas_frame()->i2w (ix1, iy1);
564 /* compensate for the ruler section and the vertical scrollbar position */
565 iy1 += _editor->get_trackview_group_vertical_offset ();
569 // hide any dependent views
571 rv->get_time_axis_view().hide_dependent_views (*rv);
574 reparent to a non scrolling group so that we can keep the
575 region selection above all time axis views.
576 reparenting means we have to move the rv as the two
577 parent groups have different coordinates.
580 rv->get_canvas_group()->property_y() = iy1 - 1;
581 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
583 rv->fake_set_opaque (true);
586 /* current view for this particular region */
587 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
588 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
590 if (pointer_order_span != 0 && !clamp_y_axis) {
592 /* INTER-TRACK MOVEMENT */
594 /* move through the height list to the track that the region is currently on */
595 vector<int32_t>::iterator j = tavs.height_list.begin ();
597 while (j != tavs.height_list.end () && x != rtv->order ()) {
603 int32_t temp_pointer_order_span = canvas_pointer_order_span;
605 if (j != tavs.height_list.end ()) {
607 /* Account for layers in the original and
608 destination tracks. If we're moving around in layers we assume
609 that only one track is involved, so it's ok to use *pointer*
612 StreamView* lv = last_pointer_view->view ();
615 /* move to the top of the last trackview */
616 if (lv->layer_display () == Stacked) {
617 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
620 StreamView* cv = current_pointer_view->view ();
623 /* move to the right layer on the current trackview */
624 if (cv->layer_display () == Stacked) {
625 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
628 /* And for being on a non-topmost layer on the new
631 while (temp_pointer_order_span > 0) {
632 /* we're moving up canvas-wise,
633 so we need to find the next track height
635 if (j != tavs.height_list.begin()) {
639 if (x != last_pointer_order) {
641 ++temp_pointer_order_span;
646 temp_pointer_order_span--;
649 while (temp_pointer_order_span < 0) {
653 if (x != last_pointer_order) {
655 --temp_pointer_order_span;
659 if (j != tavs.height_list.end()) {
663 temp_pointer_order_span++;
667 /* find out where we'll be when we move and set height accordingly */
669 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
670 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
671 rv->set_height (temp_rtv->view()->child_height());
673 /* if you un-comment the following, the region colours will follow
674 the track colours whilst dragging; personally
675 i think this can confuse things, but never mind.
678 //const GdkColor& col (temp_rtv->view->get_region_color());
679 //rv->set_color (const_cast<GdkColor&>(col));
683 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
685 /* INTER-LAYER MOVEMENT in the same track */
686 y_delta = rtv->view()->child_height () * pointer_layer_span;
691 _editor->mouse_brush_insert_region (rv, pending_region_position);
693 rv->move (x_delta, y_delta);
696 } /* foreach region */
699 _editor->cursor_group->raise_to_top();
702 if (x_delta != 0 && !_brushing) {
703 _editor->show_verbose_time_cursor (_last_frame_position, 10);
708 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
710 if (_copy && first_move) {
711 copy_regions (event);
714 RegionMotionDrag::motion (event, first_move);
718 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
720 vector<RegionView*> copies;
721 boost::shared_ptr<Diskstream> ds;
722 boost::shared_ptr<Playlist> from_playlist;
723 RegionSelection new_views;
724 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
725 PlaylistSet modified_playlists;
726 PlaylistSet frozen_playlists;
727 list <sigc::connection> modified_playlist_connections;
728 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
729 nframes64_t drag_delta;
730 bool changed_tracks, changed_position;
731 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
732 RouteTimeAxisView* source_tv;
734 if (!movement_occurred) {
740 /* all changes were made during motion event handlers */
743 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
744 copies.push_back (*i);
751 /* reverse this here so that we have the correct logic to finalize
755 if (Config->get_edit_mode() == Lock) {
756 _x_constrained = !_x_constrained;
760 if (_x_constrained) {
761 _editor->begin_reversible_command (_("fixed time region copy"));
763 _editor->begin_reversible_command (_("region copy"));
766 if (_x_constrained) {
767 _editor->begin_reversible_command (_("fixed time region drag"));
769 _editor->begin_reversible_command (_("region drag"));
773 _have_transaction = true;
775 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
776 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
778 drag_delta = _primary->region()->position() - _last_frame_position;
780 _editor->update_canvas_now ();
782 /* make a list of where each region ended up */
783 final = find_time_axis_views_and_layers ();
785 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
787 RegionView* rv = (*i);
788 RouteTimeAxisView* dest_rtv = final[*i].first;
789 layer_t dest_layer = final[*i].second;
793 if (rv->region()->locked()) {
798 if (changed_position && !_x_constrained) {
799 where = rv->region()->position() - drag_delta;
801 where = rv->region()->position();
804 boost::shared_ptr<Region> new_region;
807 /* we already made a copy */
808 new_region = rv->region();
810 /* undo the previous hide_dependent_views so that xfades don't
811 disappear on copying regions
814 //rv->get_time_axis_view().reveal_dependent_views (*rv);
816 } else if (changed_tracks && dest_rtv->playlist()) {
817 new_region = RegionFactory::create (rv->region());
820 if (changed_tracks || _copy) {
822 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
829 _editor->latest_regionviews.clear ();
831 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
833 insert_result = modified_playlists.insert (to_playlist);
835 if (insert_result.second) {
836 _editor->session()->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
839 to_playlist->add_region (new_region, where);
840 if (dest_rtv->view()->layer_display() == Stacked) {
841 new_region->set_layer (dest_layer);
842 new_region->set_pending_explicit_relayer (true);
847 if (!_editor->latest_regionviews.empty()) {
848 // XXX why just the first one ? we only expect one
849 // commented out in nick_m's canvas reworking. is that intended?
850 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
851 new_views.push_back (_editor->latest_regionviews.front());
856 motion on the same track. plonk the previously reparented region
857 back to its original canvas group (its streamview).
858 No need to do anything for copies as they are fake regions which will be deleted.
861 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
862 rv->get_canvas_group()->property_y() = 0;
864 /* just change the model */
866 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
868 if (dest_rtv->view()->layer_display() == Stacked) {
869 rv->region()->set_layer (dest_layer);
870 rv->region()->set_pending_explicit_relayer (true);
873 insert_result = modified_playlists.insert (playlist);
875 if (insert_result.second) {
876 _editor->session()->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
878 /* freeze to avoid lots of relayering in the case of a multi-region drag */
879 frozen_insert_result = frozen_playlists.insert(playlist);
881 if (frozen_insert_result.second) {
885 rv->region()->set_position (where, (void*) this);
888 if (changed_tracks && !_copy) {
890 /* get the playlist where this drag started. we can't use rv->region()->playlist()
891 because we may have copied the region and it has not been attached to a playlist.
894 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
895 ds = source_tv->get_diskstream();
896 from_playlist = ds->playlist();
900 assert (from_playlist);
902 /* moved to a different audio track, without copying */
904 /* the region that used to be in the old playlist is not
905 moved to the new one - we use a copy of it. as a result,
906 any existing editor for the region should no longer be
910 rv->hide_region_editor();
911 rv->fake_set_opaque (false);
913 /* remove the region from the old playlist */
915 insert_result = modified_playlists.insert (from_playlist);
917 if (insert_result.second) {
918 _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
921 from_playlist->remove_region (rv->region());
923 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
924 was selected in all of them, then removing it from a playlist will have removed all
925 trace of it from the selection (i.e. there were N regions selected, we removed 1,
926 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
927 corresponding regionview, and the selection is now empty).
929 this could have invalidated any and all iterators into the region selection.
931 the heuristic we use here is: if the region selection is empty, break out of the loop
932 here. if the region selection is not empty, then restart the loop because we know that
933 we must have removed at least the region(view) we've just been working on as well as any
934 that we processed on previous iterations.
936 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
940 if (_views.empty()) {
951 copies.push_back (rv);
955 if we've created new regions either by copying or moving
956 to a new track, we want to replace the old selection with the new ones
958 if (new_views.size() > 0) {
959 _editor->selection->set (new_views);
962 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
967 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
968 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
971 _editor->commit_reversible_command ();
973 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
980 RegionMotionDrag::x_move_allowed () const
982 if (Config->get_edit_mode() == Lock) {
983 /* in locked edit mode, reverse the usual meaning of _x_constrained */
984 return _x_constrained;
987 return !_x_constrained;
991 RegionMotionDrag::copy_regions (GdkEvent* event)
993 /* duplicate the regionview(s) and region(s) */
995 list<RegionView*> new_regionviews;
997 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
999 RegionView* rv = (*i);
1000 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1001 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1003 const boost::shared_ptr<const Region> original = rv->region();
1004 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1008 boost::shared_ptr<AudioRegion> audioregion_copy
1009 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1010 nrv = new AudioRegionView (*arv, audioregion_copy);
1012 boost::shared_ptr<MidiRegion> midiregion_copy
1013 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1014 nrv = new MidiRegionView (*mrv, midiregion_copy);
1019 nrv->get_canvas_group()->show ();
1020 new_regionviews.push_back (nrv);
1022 /* swap _primary to the copy */
1024 if (rv == _primary) {
1028 /* ..and deselect the one we copied */
1030 rv->set_selected (false);
1033 if (new_regionviews.empty()) {
1037 /* reflect the fact that we are dragging the copies */
1039 _views = new_regionviews;
1041 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1044 sync the canvas to what we think is its current state
1045 without it, the canvas seems to
1046 "forget" to update properly after the upcoming reparent()
1047 ..only if the mouse is in rapid motion at the time of the grab.
1048 something to do with regionview creation raking so long?
1050 _editor->update_canvas_now();
1054 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1056 /* Which trackview is this ? */
1058 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1059 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1060 (*layer) = tvp.second;
1062 if (*tv && (*tv)->layer_display() == Overlaid) {
1066 /* The region motion is only processed if the pointer is over
1070 if (!(*tv) || !(*tv)->is_track()) {
1071 /* To make sure we hide the verbose canvas cursor when the mouse is
1072 not held over and audiotrack.
1074 _editor->hide_verbose_canvas_cursor ();
1081 /** @param new_order New track order.
1082 * @param old_order Old track order.
1083 * @param visible_y_low Lowest visible order.
1084 * @return true if y movement should not happen, otherwise false.
1087 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1089 if (new_order != old_order) {
1091 /* this isn't the pointer track */
1095 /* moving up the canvas */
1096 if ( (new_order - y_span) >= tavs.visible_y_low) {
1100 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1101 int32_t visible_tracks = 0;
1102 while (visible_tracks < y_span ) {
1104 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1105 /* passing through a hidden track */
1110 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1111 /* moving to a non-track; disallow */
1117 /* moving beyond the lowest visible track; disallow */
1121 } else if (y_span < 0) {
1123 /* moving down the canvas */
1124 if ((new_order - y_span) <= tavs.visible_y_high) {
1126 int32_t visible_tracks = 0;
1128 while (visible_tracks > y_span ) {
1131 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1132 /* passing through a hidden track */
1137 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1138 /* moving to a non-track; disallow */
1145 /* moving beyond the highest visible track; disallow */
1152 /* this is the pointer's track */
1154 if ((new_order - y_span) > tavs.visible_y_high) {
1155 /* we will overflow */
1157 } else if ((new_order - y_span) < tavs.visible_y_low) {
1158 /* we will overflow */
1167 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1168 : RegionMotionDrag (e, i, p, v, b),
1171 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1173 _dest_trackview = tv;
1174 if (tv->layer_display() == Overlaid) {
1177 _dest_layer = _primary->region()->layer ();
1181 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1182 if (rtv && rtv->is_track()) {
1183 speed = rtv->get_diskstream()->speed ();
1186 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1190 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1192 RegionMotionDrag::start_grab (event, c);
1194 _pointer_frame_offset = grab_frame() - _last_frame_position;
1197 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1198 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1200 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1201 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1203 _primary = v->view()->create_region_view (r, false, false);
1205 _primary->get_canvas_group()->show ();
1206 _primary->set_position (pos, 0);
1207 _views.push_back (_primary);
1209 _last_frame_position = pos;
1211 _item = _primary->get_canvas_group ();
1212 _dest_trackview = v;
1213 _dest_layer = _primary->region()->layer ();
1216 map<RegionView*, pair<RouteTimeAxisView*, int> >
1217 RegionMotionDrag::find_time_axis_views_and_layers ()
1219 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1221 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1223 double ix1, ix2, iy1, iy2;
1224 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1225 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1226 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1228 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1229 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1237 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1239 _editor->update_canvas_now ();
1241 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1243 RouteTimeAxisView* dest_rtv = final[_primary].first;
1245 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1246 _primary->get_canvas_group()->property_y() = 0;
1248 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1250 _editor->begin_reversible_command (_("insert region"));
1251 XMLNode& before = playlist->get_state ();
1252 playlist->add_region (_primary->region (), _last_frame_position);
1253 _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1254 _editor->commit_reversible_command ();
1261 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1262 : RegionMoveDrag (e, i, p, v, false, false)
1267 struct RegionSelectionByPosition {
1268 bool operator() (RegionView*a, RegionView* b) {
1269 return a->region()->position () < b->region()->position();
1274 RegionSpliceDrag::motion (GdkEvent* event, bool)
1276 RouteTimeAxisView* tv;
1279 if (!check_possible (&tv, &layer)) {
1285 if ((current_pointer_x() - last_pointer_x()) > 0) {
1291 RegionSelection copy (_editor->selection->regions);
1293 RegionSelectionByPosition cmp;
1296 nframes64_t const pf = adjusted_current_frame (event);
1298 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1300 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1306 boost::shared_ptr<Playlist> playlist;
1308 if ((playlist = atv->playlist()) == 0) {
1312 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1317 if (pf < (*i)->region()->last_frame() + 1) {
1321 if (pf > (*i)->region()->first_frame()) {
1327 playlist->shuffle ((*i)->region(), dir);
1332 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1338 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1346 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1348 _dest_trackview = _view;
1350 Drag::start_grab (event);
1355 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1358 // TODO: create region-create-drag region view here
1361 // TODO: resize region-create-drag region view here
1365 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1367 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1373 if (!movement_occurred) {
1374 mtv->add_region (grab_frame ());
1376 motion (event, false);
1377 // TODO: create region-create-drag region here
1381 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1389 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1392 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1394 Drag::start_grab (event);
1396 region = &cnote->region_view();
1398 double region_start = region->get_position_pixels();
1399 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1401 if (grab_x() <= middle_point) {
1402 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1405 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1409 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1411 if (event->motion.state & Keyboard::PrimaryModifier) {
1417 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1419 if (ms.size() > 1) {
1420 /* has to be relative, may make no sense otherwise */
1424 region->note_selected (cnote, true);
1426 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1427 MidiRegionSelection::iterator next;
1430 (*r)->begin_resizing (at_front);
1436 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1438 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1439 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1440 (*r)->update_resizing (at_front, current_pointer_x() - grab_x(), relative);
1445 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1447 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1448 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1449 (*r)->commit_resizing (at_front, current_pointer_x() - grab_x(), relative);
1454 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1460 RegionGainDrag::finished (GdkEvent *, bool)
1465 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1466 : RegionDrag (e, i, p, v)
1472 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1475 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1476 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1478 if (tv && tv->is_track()) {
1479 speed = tv->get_diskstream()->speed();
1482 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1483 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1484 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1486 Drag::start_grab (event, _editor->trimmer_cursor);
1488 nframes64_t const pf = adjusted_current_frame (event);
1490 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1491 _operation = ContentsTrim;
1493 /* These will get overridden for a point trim.*/
1494 if (pf < (region_start + region_length/2)) {
1495 /* closer to start */
1496 _operation = StartTrim;
1497 } else if (pf > (region_end - region_length/2)) {
1499 _operation = EndTrim;
1503 switch (_operation) {
1505 _editor->show_verbose_time_cursor (region_start, 10);
1508 _editor->show_verbose_time_cursor (region_end, 10);
1511 _editor->show_verbose_time_cursor (pf, 10);
1517 TrimDrag::motion (GdkEvent* event, bool first_move)
1519 RegionView* rv = _primary;
1520 nframes64_t frame_delta = 0;
1522 bool left_direction;
1523 bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1525 /* snap modifier works differently here..
1526 its current state has to be passed to the
1527 various trim functions in order to work properly
1531 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1532 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1533 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1535 if (tv && tv->is_track()) {
1536 speed = tv->get_diskstream()->speed();
1539 nframes64_t const pf = adjusted_current_frame (event);
1541 if (last_pointer_frame() > pf) {
1542 left_direction = true;
1544 left_direction = false;
1551 switch (_operation) {
1553 trim_type = "Region start trim";
1556 trim_type = "Region end trim";
1559 trim_type = "Region content trim";
1563 _editor->begin_reversible_command (trim_type);
1564 _have_transaction = true;
1566 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1567 (*i)->fake_set_opaque(false);
1568 (*i)->region()->freeze ();
1570 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1573 arv->temporarily_hide_envelope ();
1576 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1577 insert_result = _editor->motion_frozen_playlists.insert (pl);
1579 if (insert_result.second) {
1580 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1586 if (pf == last_pointer_frame()) {
1590 /* XXX i hope to god that we can really conclude this ... */
1591 _have_transaction = true;
1593 if (left_direction) {
1594 frame_delta = (last_pointer_frame() - pf);
1596 frame_delta = (pf - last_pointer_frame());
1599 bool non_overlap_trim = false;
1601 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1602 non_overlap_trim = true;
1605 switch (_operation) {
1607 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1611 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1612 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1618 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1622 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1623 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1630 bool swap_direction = false;
1632 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1633 swap_direction = true;
1636 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 (pf, 10);
1658 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1660 if (movement_occurred) {
1661 motion (event, false);
1663 if (!_editor->selection->selected (_primary)) {
1664 _editor->thaw_region_after_trim (*_primary);
1667 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1668 _editor->thaw_region_after_trim (**i);
1669 (*i)->fake_set_opaque (true);
1672 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1674 if (_have_transaction) {
1675 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1679 _editor->motion_frozen_playlists.clear ();
1681 if (_have_transaction) {
1682 _editor->commit_reversible_command();
1686 /* no mouse movement */
1687 _editor->point_trim (event);
1691 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1695 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1700 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1703 // create a dummy marker for visual representation of moving the copy.
1704 // The actual copying is not done before we reach the finish callback.
1706 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1707 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1708 *new MeterSection (_marker->meter()));
1710 _item = &new_marker->the_item ();
1711 _marker = new_marker;
1715 MetricSection& section (_marker->meter());
1717 if (!section.movable()) {
1723 Drag::start_grab (event, cursor);
1725 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1727 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1731 MeterMarkerDrag::motion (GdkEvent* event, bool)
1733 nframes64_t const pf = adjusted_current_frame (event);
1735 if (pf == last_pointer_frame()) {
1739 _marker->set_position (pf);
1741 _editor->show_verbose_time_cursor (pf, 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 (adjusted_current_frame (event), 10);
1819 TempoMarkerDrag::motion (GdkEvent* event, bool)
1821 nframes64_t const pf = adjusted_current_frame (event);
1823 if (pf == last_pointer_frame()) {
1827 /* OK, we've moved far enough to make it worth actually move the thing. */
1829 _marker->set_position (pf);
1831 _editor->show_verbose_time_cursor (pf, 10);
1835 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1837 if (!movement_occurred) {
1841 motion (event, false);
1845 TempoMap& map (_editor->session()->tempo_map());
1846 map.bbt_time (last_pointer_frame(), when);
1848 if (_copy == true) {
1849 _editor->begin_reversible_command (_("copy tempo mark"));
1850 XMLNode &before = map.get_state();
1851 map.add_tempo (_marker->tempo(), when);
1852 XMLNode &after = map.get_state();
1853 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1854 _editor->commit_reversible_command ();
1856 // delete the dummy marker we used for visual representation of copying.
1857 // a new visual marker will show up automatically.
1860 _editor->begin_reversible_command (_("move tempo mark"));
1861 XMLNode &before = map.get_state();
1862 map.move_tempo (_marker->tempo(), when);
1863 XMLNode &after = map.get_state();
1864 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1865 _editor->commit_reversible_command ();
1870 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1874 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1879 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1881 Drag::start_grab (event, c);
1885 nframes64_t where = _editor->event_frame (event, 0, 0);
1887 _editor->snap_to_with_modifier (where, event);
1888 _editor->playhead_cursor->set_position (where);
1892 if (_cursor == _editor->playhead_cursor) {
1893 _editor->_dragging_playhead = true;
1895 if (_editor->session() && _was_rolling && _stop) {
1896 _editor->session()->request_stop ();
1899 if (_editor->session() && _editor->session()->is_auditioning()) {
1900 _editor->session()->cancel_audition ();
1904 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1908 CursorDrag::motion (GdkEvent* event, bool)
1910 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1912 if (adjusted_frame == last_pointer_frame()) {
1916 _cursor->set_position (adjusted_frame);
1918 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1921 _editor->update_canvas_now ();
1923 _editor->UpdateAllTransportClocks (_cursor->current_frame);
1927 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1929 _editor->_dragging_playhead = false;
1931 if (!movement_occurred && _stop) {
1935 motion (event, false);
1937 if (_item == &_editor->playhead_cursor->canvas_item) {
1938 if (_editor->session()) {
1939 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1940 _editor->_pending_locate_request = true;
1945 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1946 : RegionDrag (e, i, p, v)
1952 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1954 Drag::start_grab (event, cursor);
1956 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1957 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1959 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
1960 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1964 FadeInDrag::motion (GdkEvent* event, bool)
1966 nframes64_t fade_length;
1968 nframes64_t const pos = adjusted_current_frame (event);
1970 boost::shared_ptr<Region> region = _primary->region ();
1972 if (pos < (region->position() + 64)) {
1973 fade_length = 64; // this should be a minimum defined somewhere
1974 } else if (pos > region->last_frame()) {
1975 fade_length = region->length();
1977 fade_length = pos - region->position();
1980 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1982 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1988 tmp->reset_fade_in_shape_width (fade_length);
1991 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
1995 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
1997 if (!movement_occurred) {
2001 nframes64_t fade_length;
2003 nframes64_t const pos = adjusted_current_frame (event);
2005 boost::shared_ptr<Region> region = _primary->region ();
2007 if (pos < (region->position() + 64)) {
2008 fade_length = 64; // this should be a minimum defined somewhere
2009 } else if (pos > region->last_frame()) {
2010 fade_length = region->length();
2012 fade_length = pos - region->position();
2015 _editor->begin_reversible_command (_("change fade in length"));
2017 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2019 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2025 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2026 XMLNode &before = alist->get_state();
2028 tmp->audio_region()->set_fade_in_length (fade_length);
2029 tmp->audio_region()->set_fade_in_active (true);
2031 XMLNode &after = alist->get_state();
2032 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2035 _editor->commit_reversible_command ();
2038 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2039 : RegionDrag (e, i, p, v)
2045 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2047 Drag::start_grab (event, cursor);
2049 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2050 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2052 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2053 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2057 FadeOutDrag::motion (GdkEvent* event, bool)
2059 nframes64_t fade_length;
2061 nframes64_t const pos = adjusted_current_frame (event);
2063 boost::shared_ptr<Region> region = _primary->region ();
2065 if (pos > (region->last_frame() - 64)) {
2066 fade_length = 64; // this should really be a minimum fade defined somewhere
2068 else if (pos < region->position()) {
2069 fade_length = region->length();
2072 fade_length = region->last_frame() - pos;
2075 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2077 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2083 tmp->reset_fade_out_shape_width (fade_length);
2086 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2090 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2092 if (!movement_occurred) {
2096 nframes64_t fade_length;
2098 nframes64_t const pos = adjusted_current_frame (event);
2100 boost::shared_ptr<Region> region = _primary->region ();
2102 if (pos > (region->last_frame() - 64)) {
2103 fade_length = 64; // this should really be a minimum fade defined somewhere
2105 else if (pos < region->position()) {
2106 fade_length = region->length();
2109 fade_length = region->last_frame() - pos;
2112 _editor->begin_reversible_command (_("change fade out length"));
2114 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2116 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2122 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2123 XMLNode &before = alist->get_state();
2125 tmp->audio_region()->set_fade_out_length (fade_length);
2126 tmp->audio_region()->set_fade_out_active (true);
2128 XMLNode &after = alist->get_state();
2129 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2132 _editor->commit_reversible_command ();
2135 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2138 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2141 _points.push_back (Gnome::Art::Point (0, 0));
2142 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2144 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2145 _line->property_width_pixels() = 1;
2146 _line->property_points () = _points;
2149 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2152 MarkerDrag::~MarkerDrag ()
2154 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2160 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2162 Drag::start_grab (event, cursor);
2166 Location *location = _editor->find_location_from_marker (_marker, is_start);
2167 _editor->_dragging_edit_point = true;
2169 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2171 update_item (location);
2173 // _drag_line->show();
2174 // _line->raise_to_top();
2177 _editor->show_verbose_time_cursor (location->start(), 10);
2179 _editor->show_verbose_time_cursor (location->end(), 10);
2182 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2185 case Selection::Toggle:
2186 _editor->selection->toggle (_marker);
2188 case Selection::Set:
2189 if (!_editor->selection->selected (_marker)) {
2190 _editor->selection->set (_marker);
2193 case Selection::Extend:
2195 Locations::LocationList ll;
2196 list<Marker*> to_add;
2198 _editor->selection->markers.range (s, e);
2199 s = min (_marker->position(), s);
2200 e = max (_marker->position(), e);
2203 if (e < max_frames) {
2206 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2207 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2208 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2211 to_add.push_back (lm->start);
2214 to_add.push_back (lm->end);
2218 if (!to_add.empty()) {
2219 _editor->selection->add (to_add);
2223 case Selection::Add:
2224 _editor->selection->add (_marker);
2228 /* set up copies for us to manipulate during the drag */
2230 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2231 Location *l = _editor->find_location_from_marker (*i, is_start);
2232 _copied_locations.push_back (new Location (*l));
2237 MarkerDrag::motion (GdkEvent* event, bool)
2239 nframes64_t f_delta = 0;
2241 bool move_both = false;
2243 Location *real_location;
2244 Location *copy_location = 0;
2246 nframes64_t const newframe = adjusted_current_frame (event);
2248 nframes64_t next = newframe;
2250 if (newframe == last_pointer_frame()) {
2254 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2258 MarkerSelection::iterator i;
2259 list<Location*>::iterator x;
2261 /* find the marker we're dragging, and compute the delta */
2263 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2264 x != _copied_locations.end() && i != _editor->selection->markers.end();
2270 if (marker == _marker) {
2272 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2277 if (real_location->is_mark()) {
2278 f_delta = newframe - copy_location->start();
2282 switch (marker->type()) {
2284 case Marker::LoopStart:
2285 case Marker::PunchIn:
2286 f_delta = newframe - copy_location->start();
2290 case Marker::LoopEnd:
2291 case Marker::PunchOut:
2292 f_delta = newframe - copy_location->end();
2295 /* what kind of marker is this ? */
2303 if (i == _editor->selection->markers.end()) {
2304 /* hmm, impossible - we didn't find the dragged marker */
2308 /* now move them all */
2310 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2311 x != _copied_locations.end() && i != _editor->selection->markers.end();
2317 /* call this to find out if its the start or end */
2319 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2323 if (real_location->locked()) {
2327 if (copy_location->is_mark()) {
2331 copy_location->set_start (copy_location->start() + f_delta);
2335 nframes64_t new_start = copy_location->start() + f_delta;
2336 nframes64_t new_end = copy_location->end() + f_delta;
2338 if (is_start) { // start-of-range marker
2341 copy_location->set_start (new_start);
2342 copy_location->set_end (new_end);
2343 } else if (new_start < copy_location->end()) {
2344 copy_location->set_start (new_start);
2346 _editor->snap_to (next, 1, true);
2347 copy_location->set_end (next);
2348 copy_location->set_start (newframe);
2351 } else { // end marker
2354 copy_location->set_end (new_end);
2355 copy_location->set_start (new_start);
2356 } else if (new_end > copy_location->start()) {
2357 copy_location->set_end (new_end);
2358 } else if (newframe > 0) {
2359 _editor->snap_to (next, -1, true);
2360 copy_location->set_start (next);
2361 copy_location->set_end (newframe);
2366 update_item (copy_location);
2368 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2371 lm->set_position (copy_location->start(), copy_location->end());
2375 assert (!_copied_locations.empty());
2377 _editor->show_verbose_time_cursor (newframe, 10);
2380 _editor->update_canvas_now ();
2385 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2387 if (!movement_occurred) {
2389 /* just a click, do nothing but finish
2390 off the selection process
2393 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2396 case Selection::Set:
2397 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2398 _editor->selection->set (_marker);
2402 case Selection::Toggle:
2403 case Selection::Extend:
2404 case Selection::Add:
2411 _editor->_dragging_edit_point = false;
2413 _editor->begin_reversible_command ( _("move marker") );
2414 XMLNode &before = _editor->session()->locations()->get_state();
2416 MarkerSelection::iterator i;
2417 list<Location*>::iterator x;
2420 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2421 x != _copied_locations.end() && i != _editor->selection->markers.end();
2424 Location * location = _editor->find_location_from_marker (*i, is_start);
2428 if (location->locked()) {
2432 if (location->is_mark()) {
2433 location->set_start ((*x)->start());
2435 location->set ((*x)->start(), (*x)->end());
2440 XMLNode &after = _editor->session()->locations()->get_state();
2441 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2442 _editor->commit_reversible_command ();
2448 MarkerDrag::update_item (Location* location)
2450 double const x1 = _editor->frame_to_pixel (location->start());
2452 _points.front().set_x(x1);
2453 _points.back().set_x(x1);
2454 _line->property_points() = _points;
2457 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2459 _cumulative_x_drag (0),
2460 _cumulative_y_drag (0)
2462 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2468 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2470 Drag::start_grab (event, _editor->fader_cursor);
2472 // start the grab at the center of the control point so
2473 // the point doesn't 'jump' to the mouse after the first drag
2474 _time_axis_view_grab_x = _point->get_x();
2475 _time_axis_view_grab_y = _point->get_y();
2476 nframes64_t grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x);
2478 float const fraction = 1 - (_point->get_y() / _point->line().height());
2480 _point->line().start_drag_single (_point, grab_frame, fraction);
2482 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2483 event->button.x + 10, event->button.y + 10);
2485 _editor->show_verbose_canvas_cursor ();
2489 ControlPointDrag::motion (GdkEvent* event, bool)
2491 double dx = current_pointer_x() - last_pointer_x();
2492 double dy = current_pointer_y() - last_pointer_y();
2494 if (event->button.state & Keyboard::SecondaryModifier) {
2499 /* coordinate in TimeAxisView's space */
2500 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2501 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2503 // calculate zero crossing point. back off by .01 to stay on the
2504 // positive side of zero
2505 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2507 // make sure we hit zero when passing through
2508 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2512 if (_x_constrained) {
2513 cx = _time_axis_view_grab_x;
2515 if (_y_constrained) {
2516 cy = _time_axis_view_grab_y;
2519 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2520 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2524 cy = min ((double) _point->line().height(), cy);
2526 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2528 if (!_x_constrained) {
2529 _editor->snap_to_with_modifier (cx_frames, event);
2532 float const fraction = 1.0 - (cy / _point->line().height());
2534 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2536 _point->line().drag_motion (cx_frames, fraction, push);
2538 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2542 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2544 if (!movement_occurred) {
2548 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2549 _editor->reset_point_selection ();
2553 motion (event, false);
2555 _point->line().end_drag ();
2559 ControlPointDrag::active (Editing::MouseMode m)
2561 if (m == Editing::MouseGain) {
2562 /* always active in mouse gain */
2566 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2567 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2570 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2573 _cumulative_y_drag (0)
2578 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2580 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2583 _item = &_line->grab_item ();
2585 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2586 origin, and ditto for y.
2589 double cx = event->button.x;
2590 double cy = event->button.y;
2592 _line->parent_group().w2i (cx, cy);
2594 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2599 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2600 /* no adjacent points */
2604 Drag::start_grab (event, _editor->fader_cursor);
2606 /* store grab start in parent frame */
2608 _time_axis_view_grab_x = cx;
2609 _time_axis_view_grab_y = cy;
2611 double fraction = 1.0 - (cy / _line->height());
2613 _line->start_drag_line (before, after, fraction);
2615 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2616 event->button.x + 10, event->button.y + 10);
2618 _editor->show_verbose_canvas_cursor ();
2622 LineDrag::motion (GdkEvent* event, bool)
2624 double dy = current_pointer_y() - last_pointer_y();
2626 if (event->button.state & Keyboard::SecondaryModifier) {
2630 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2632 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2635 cy = min ((double) _line->height(), cy);
2637 double const fraction = 1.0 - (cy / _line->height());
2641 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2647 /* we are ignoring x position for this drag, so we can just pass in 0 */
2648 _line->drag_motion (0, fraction, push);
2650 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2654 LineDrag::finished (GdkEvent* event, bool)
2656 motion (event, false);
2661 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2663 Drag::start_grab (event);
2664 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2668 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2675 /* use a bigger drag threshold than the default */
2677 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2679 if (abs ((int) (pf - grab_frame())) < 8) {
2683 nframes64_t grab = grab_frame ();
2684 if (Config->get_rubberbanding_snaps_to_grid ()) {
2685 _editor->snap_to_with_modifier (grab, event);
2688 /* base start and end on initial click position */
2698 if (current_pointer_y() < grab_y()) {
2699 y1 = current_pointer_y();
2702 y2 = current_pointer_y();
2707 if (start != end || y1 != y2) {
2709 double x1 = _editor->frame_to_pixel (start);
2710 double x2 = _editor->frame_to_pixel (end);
2712 _editor->rubberband_rect->property_x1() = x1;
2713 _editor->rubberband_rect->property_y1() = y1;
2714 _editor->rubberband_rect->property_x2() = x2;
2715 _editor->rubberband_rect->property_y2() = y2;
2717 _editor->rubberband_rect->show();
2718 _editor->rubberband_rect->raise_to_top();
2720 _editor->show_verbose_time_cursor (pf, 10);
2725 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2727 if (movement_occurred) {
2729 motion (event, false);
2732 if (current_pointer_y() < grab_y()) {
2733 y1 = current_pointer_y();
2736 y2 = current_pointer_y();
2741 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2744 _editor->begin_reversible_command (_("rubberband selection"));
2746 if (grab_frame() < last_pointer_frame()) {
2747 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
2749 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
2753 _editor->commit_reversible_command ();
2757 if (!getenv("ARDOUR_SAE")) {
2758 _editor->selection->clear_tracks();
2760 _editor->selection->clear_regions();
2761 _editor->selection->clear_points ();
2762 _editor->selection->clear_lines ();
2765 _editor->rubberband_rect->hide();
2769 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2771 Drag::start_grab (event);
2773 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2777 TimeFXDrag::motion (GdkEvent* event, bool)
2779 RegionView* rv = _primary;
2781 nframes64_t const pf = adjusted_current_frame (event);
2783 if (pf == last_pointer_frame()) {
2787 if (pf > rv->region()->position()) {
2788 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2791 _editor->show_verbose_time_cursor (pf, 10);
2795 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2797 _primary->get_time_axis_view().hide_timestretch ();
2799 if (!movement_occurred) {
2803 if (last_pointer_frame() < _primary->region()->position()) {
2804 /* backwards drag of the left edge - not usable */
2808 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2810 float percentage = (double) newlen / (double) _primary->region()->length();
2812 #ifndef USE_RUBBERBAND
2813 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2814 if (_primary->region()->data_type() == DataType::AUDIO) {
2815 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2819 _editor->begin_reversible_command (_("timestretch"));
2821 // XXX how do timeFX on multiple regions ?
2826 if (!_editor->time_stretch (rs, percentage) == 0) {
2827 error << _("An error occurred while executing time stretch operation") << endmsg;
2832 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2834 Drag::start_grab (event);
2838 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2844 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2846 if (movement_occurred && _editor->session()) {
2847 /* make sure we stop */
2848 _editor->session()->request_transport_speed (0.0);
2852 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2856 , _original_pointer_time_axis (-1)
2857 , _last_pointer_time_axis (-1)
2863 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2865 nframes64_t start = 0;
2866 nframes64_t end = 0;
2868 if (_editor->session() == 0) {
2872 Gdk::Cursor* cursor = 0;
2874 switch (_operation) {
2875 case CreateSelection:
2876 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2881 cursor = _editor->selector_cursor;
2882 Drag::start_grab (event, cursor);
2885 case SelectionStartTrim:
2886 if (_editor->clicked_axisview) {
2887 _editor->clicked_axisview->order_selection_trims (_item, true);
2889 Drag::start_grab (event, _editor->trimmer_cursor);
2890 start = _editor->selection->time[_editor->clicked_selection].start;
2891 _pointer_frame_offset = grab_frame() - start;
2894 case SelectionEndTrim:
2895 if (_editor->clicked_axisview) {
2896 _editor->clicked_axisview->order_selection_trims (_item, false);
2898 Drag::start_grab (event, _editor->trimmer_cursor);
2899 end = _editor->selection->time[_editor->clicked_selection].end;
2900 _pointer_frame_offset = grab_frame() - end;
2904 start = _editor->selection->time[_editor->clicked_selection].start;
2905 Drag::start_grab (event, cursor);
2906 _pointer_frame_offset = grab_frame() - start;
2910 if (_operation == SelectionMove) {
2911 _editor->show_verbose_time_cursor (start, 10);
2913 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2916 _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
2920 SelectionDrag::motion (GdkEvent* event, bool first_move)
2922 nframes64_t start = 0;
2923 nframes64_t end = 0;
2926 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (current_pointer_y ());
2927 if (pending_time_axis.first == 0) {
2931 nframes64_t const pending_position = adjusted_current_frame (event);
2933 /* only alter selection if things have changed */
2935 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
2939 switch (_operation) {
2940 case CreateSelection:
2942 nframes64_t grab = grab_frame ();
2945 _editor->snap_to (grab);
2948 if (pending_position < grab_frame()) {
2949 start = pending_position;
2952 end = pending_position;
2956 /* first drag: Either add to the selection
2957 or create a new selection
2962 _editor->begin_reversible_command (_("range selection"));
2963 _have_transaction = true;
2966 /* adding to the selection */
2967 _editor->selection->add (_editor->clicked_axisview);
2968 _editor->clicked_selection = _editor->selection->add (start, end);
2973 if (!_editor->selection->selected (_editor->clicked_axisview)) {
2974 _editor->selection->set (_editor->clicked_axisview);
2977 _editor->clicked_selection = _editor->selection->set (start, end);
2981 /* select the track that we're in */
2982 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
2983 _editor->selection->add (pending_time_axis.first);
2984 _added_time_axes.push_back (pending_time_axis.first);
2987 /* deselect any tracks that this drag no longer includes, being careful to only deselect
2988 tracks that we selected in the first place.
2991 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
2992 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
2994 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
2995 while (i != _added_time_axes.end()) {
2997 list<TimeAxisView*>::iterator tmp = i;
3000 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3001 _editor->selection->remove (*i);
3002 _added_time_axes.remove (*i);
3011 case SelectionStartTrim:
3014 _editor->begin_reversible_command (_("trim selection start"));
3015 _have_transaction = true;
3018 start = _editor->selection->time[_editor->clicked_selection].start;
3019 end = _editor->selection->time[_editor->clicked_selection].end;
3021 if (pending_position > end) {
3024 start = pending_position;
3028 case SelectionEndTrim:
3031 _editor->begin_reversible_command (_("trim selection end"));
3032 _have_transaction = true;
3035 start = _editor->selection->time[_editor->clicked_selection].start;
3036 end = _editor->selection->time[_editor->clicked_selection].end;
3038 if (pending_position < start) {
3041 end = pending_position;
3049 _editor->begin_reversible_command (_("move selection"));
3050 _have_transaction = true;
3053 start = _editor->selection->time[_editor->clicked_selection].start;
3054 end = _editor->selection->time[_editor->clicked_selection].end;
3056 length = end - start;
3058 start = pending_position;
3059 _editor->snap_to (start);
3061 end = start + length;
3066 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3067 _editor->start_canvas_autoscroll (1, 0);
3071 _editor->selection->replace (_editor->clicked_selection, start, end);
3074 if (_operation == SelectionMove) {
3075 _editor->show_verbose_time_cursor(start, 10);
3077 _editor->show_verbose_time_cursor(pending_position, 10);
3082 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3084 Session* s = _editor->session();
3086 if (movement_occurred) {
3087 motion (event, false);
3088 /* XXX this is not object-oriented programming at all. ick */
3089 if (_editor->selection->time.consolidate()) {
3090 _editor->selection->TimeChanged ();
3093 if (_have_transaction) {
3094 _editor->commit_reversible_command ();
3097 /* XXX what if its a music time selection? */
3098 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3099 s->request_play_range (&_editor->selection->time, true);
3104 /* just a click, no pointer movement.*/
3106 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3107 _editor->selection->clear_time();
3110 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3111 _editor->selection->set (_editor->clicked_axisview);
3114 if (s && s->get_play_range () && s->transport_rolling()) {
3115 s->request_stop (false, false);
3120 _editor->stop_canvas_autoscroll ();
3123 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3128 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3129 _drag_rect->hide ();
3131 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3132 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3136 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3138 if (_editor->session() == 0) {
3142 Gdk::Cursor* cursor = 0;
3144 if (!_editor->temp_location) {
3145 _editor->temp_location = new Location;
3148 switch (_operation) {
3149 case CreateRangeMarker:
3150 case CreateTransportMarker:
3151 case CreateCDMarker:
3153 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3158 cursor = _editor->selector_cursor;
3162 Drag::start_grab (event, cursor);
3164 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3168 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3170 nframes64_t start = 0;
3171 nframes64_t end = 0;
3172 ArdourCanvas::SimpleRect *crect;
3174 switch (_operation) {
3175 case CreateRangeMarker:
3176 crect = _editor->range_bar_drag_rect;
3178 case CreateTransportMarker:
3179 crect = _editor->transport_bar_drag_rect;
3181 case CreateCDMarker:
3182 crect = _editor->cd_marker_bar_drag_rect;
3185 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3190 nframes64_t const pf = adjusted_current_frame (event);
3192 /* only alter selection if the current frame is
3193 different from the last frame position.
3196 if (pf == last_pointer_frame()) {
3200 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3201 nframes64_t grab = grab_frame ();
3202 _editor->snap_to (grab);
3204 if (pf < grab_frame()) {
3212 /* first drag: Either add to the selection
3213 or create a new selection.
3218 _editor->temp_location->set (start, end);
3222 update_item (_editor->temp_location);
3224 //_drag_rect->raise_to_top();
3229 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3230 _editor->start_canvas_autoscroll (1, 0);
3234 _editor->temp_location->set (start, end);
3236 double x1 = _editor->frame_to_pixel (start);
3237 double x2 = _editor->frame_to_pixel (end);
3238 crect->property_x1() = x1;
3239 crect->property_x2() = x2;
3241 update_item (_editor->temp_location);
3244 _editor->show_verbose_time_cursor (pf, 10);
3249 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3251 Location * newloc = 0;
3255 if (movement_occurred) {
3256 motion (event, false);
3259 switch (_operation) {
3260 case CreateRangeMarker:
3261 case CreateCDMarker:
3263 _editor->begin_reversible_command (_("new range marker"));
3264 XMLNode &before = _editor->session()->locations()->get_state();
3265 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3266 if (_operation == CreateCDMarker) {
3267 flags = Location::IsRangeMarker | Location::IsCDMarker;
3268 _editor->cd_marker_bar_drag_rect->hide();
3271 flags = Location::IsRangeMarker;
3272 _editor->range_bar_drag_rect->hide();
3274 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3275 _editor->session()->locations()->add (newloc, true);
3276 XMLNode &after = _editor->session()->locations()->get_state();
3277 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3278 _editor->commit_reversible_command ();
3282 case CreateTransportMarker:
3283 // popup menu to pick loop or punch
3284 _editor->new_transport_marker_context_menu (&event->button, _item);
3288 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3290 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3295 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3297 if (end == max_frames) {
3298 end = _editor->session()->current_end_frame ();
3301 if (start == max_frames) {
3302 start = _editor->session()->current_start_frame ();
3305 switch (_editor->mouse_mode) {
3307 /* find the two markers on either side and then make the selection from it */
3308 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3312 /* find the two markers on either side of the click and make the range out of it */
3313 _editor->selection->set (start, end);
3322 _editor->stop_canvas_autoscroll ();
3328 RangeMarkerBarDrag::update_item (Location* location)
3330 double const x1 = _editor->frame_to_pixel (location->start());
3331 double const x2 = _editor->frame_to_pixel (location->end());
3333 _drag_rect->property_x1() = x1;
3334 _drag_rect->property_x2() = x2;
3338 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3340 Drag::start_grab (event, _editor->zoom_cursor);
3341 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3345 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3350 nframes64_t const pf = adjusted_current_frame (event);
3352 if (pf == last_pointer_frame()) {
3356 nframes64_t grab = grab_frame ();
3357 _editor->snap_to_with_modifier (grab, event);
3359 /* base start and end on initial click position */
3371 _editor->zoom_rect->show();
3372 _editor->zoom_rect->raise_to_top();
3375 _editor->reposition_zoom_rect(start, end);
3377 _editor->show_verbose_time_cursor (pf, 10);
3382 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3384 if (movement_occurred) {
3385 motion (event, false);
3387 if (grab_frame() < last_pointer_frame()) {
3388 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3390 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3393 _editor->temporal_zoom_to_frame (false, grab_frame());
3395 temporal_zoom_step (false);
3396 center_screen (grab_frame());
3400 _editor->zoom_rect->hide();
3403 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3406 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3407 region = &cnote->region_view();
3411 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3413 Drag::start_grab (event);
3416 drag_delta_note = 0;
3421 event_x = current_pointer_x();
3422 event_y = current_pointer_y();
3424 _item->property_parent().get_value()->w2i(event_x, event_y);
3426 last_x = region->snap_to_pixel(event_x);
3429 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3431 if (!(was_selected = cnote->selected())) {
3433 /* tertiary-click means extend selection - we'll do that on button release,
3434 so don't add it here, because otherwise we make it hard to figure
3435 out the "extend-to" range.
3438 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3441 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3444 region->note_selected (cnote, true);
3446 region->unique_select (cnote);
3453 NoteDrag::motion (GdkEvent*, bool)
3455 MidiStreamView* streamview = region->midi_stream_view();
3459 event_x = current_pointer_x();
3460 event_y = current_pointer_y();
3462 _item->property_parent().get_value()->w2i(event_x, event_y);
3464 event_x = region->snap_to_pixel(event_x);
3466 double dx = event_x - last_x;
3467 double dy = event_y - last_y;
3472 // Snap to note rows
3474 if (abs (dy) < streamview->note_height()) {
3477 int8_t this_delta_note;
3479 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3481 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3483 drag_delta_note -= this_delta_note;
3484 dy = streamview->note_height() * this_delta_note;
3485 last_y = last_y + dy;
3489 region->move_selection (dx, dy);
3491 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3493 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3494 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3495 _editor->show_verbose_canvas_cursor_with (buf);
3500 NoteDrag::finished (GdkEvent* ev, bool moved)
3502 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3505 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3508 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3510 region->note_deselected (cnote);
3513 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3514 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3516 if (!extend && !add && region->selection_size() > 1) {
3517 region->unique_select(cnote);
3518 } else if (extend) {
3519 region->note_selected (cnote, true, true);
3521 /* it was added during button press */
3526 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3530 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3533 , _nothing_to_drag (false)
3535 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3538 _line = _atav->line ();
3542 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3544 Drag::start_grab (event, cursor);
3546 list<ControlPoint*> points;
3548 XMLNode* state = &_line->get_state ();
3550 if (_ranges.empty()) {
3552 uint32_t const N = _line->npoints ();
3553 for (uint32_t i = 0; i < N; ++i) {
3554 points.push_back (_line->nth (i));
3559 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3560 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3562 /* fade into and out of the region that we're dragging;
3563 64 samples length plucked out of thin air.
3565 nframes64_t const h = (j->start + j->end) / 2;
3566 nframes64_t a = j->start + 64;
3570 nframes64_t b = j->end - 64;
3575 the_list->add (j->start, the_list->eval (j->start));
3576 _line->add_always_in_view (j->start);
3577 the_list->add (a, the_list->eval (a));
3578 _line->add_always_in_view (a);
3579 the_list->add (b, the_list->eval (b));
3580 _line->add_always_in_view (b);
3581 the_list->add (j->end, the_list->eval (j->end));
3582 _line->add_always_in_view (j->end);
3585 uint32_t const N = _line->npoints ();
3586 for (uint32_t i = 0; i < N; ++i) {
3588 ControlPoint* p = _line->nth (i);
3590 list<AudioRange>::const_iterator j = _ranges.begin ();
3591 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3595 if (j != _ranges.end()) {
3596 points.push_back (p);
3601 if (points.empty()) {
3602 _nothing_to_drag = true;
3606 _line->start_drag_multiple (points, 1 - (current_pointer_y() / _line->height ()), state);
3610 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3612 if (_nothing_to_drag) {
3616 float const f = 1 - (current_pointer_y() / _line->height());
3618 /* we are ignoring x position for this drag, so we can just pass in 0 */
3619 _line->drag_motion (0, f, false);
3623 AutomationRangeDrag::finished (GdkEvent* event, bool)
3625 if (_nothing_to_drag) {
3629 motion (event, false);
3631 _line->clear_always_in_view ();