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 "control_point.h"
35 #include "region_gain_line.h"
36 #include "editor_drag.h"
37 #include "audio_time_axis.h"
38 #include "midi_time_axis.h"
39 #include "canvas-note.h"
40 #include "selection.h"
41 #include "midi_selection.h"
44 using namespace ARDOUR;
48 using namespace Editing;
49 using namespace ArdourCanvas;
51 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
53 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
56 , _pointer_frame_offset (0)
58 , _last_pointer_frame (0)
59 , _current_pointer_frame (0)
60 , _have_transaction (false)
61 , _had_movement (false)
62 , _move_threshold_passed (false)
68 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
74 cursor = _editor->which_grabber_cursor ();
77 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
81 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
84 cursor = _editor->which_grabber_cursor ();
87 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
89 if (Keyboard::is_button2_event (&event->button)) {
90 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
91 _y_constrained = true;
92 _x_constrained = false;
94 _y_constrained = false;
95 _x_constrained = true;
98 _x_constrained = false;
99 _y_constrained = false;
102 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
103 _last_pointer_frame = _grab_frame;
104 _current_pointer_frame = _grab_frame;
105 _current_pointer_x = _grab_x;
106 _current_pointer_y = _grab_y;
107 _last_pointer_x = _current_pointer_x;
108 _last_pointer_y = _current_pointer_y;
112 _item->i2w (_original_x, _original_y);
114 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
118 if (_editor->session && _editor->session->transport_rolling()) {
121 _was_rolling = false;
124 switch (_editor->snap_type()) {
125 case SnapToRegionStart:
126 case SnapToRegionEnd:
127 case SnapToRegionSync:
128 case SnapToRegionBoundary:
129 _editor->build_region_boundary_cache ();
136 /** @param event GDK event, or 0.
137 * @return true if some movement occurred, otherwise false.
140 Drag::end_grab (GdkEvent* event)
144 _editor->stop_canvas_autoscroll ();
146 _item->ungrab (event ? event->button.time : 0);
148 _last_pointer_x = _current_pointer_x;
149 _last_pointer_y = _current_pointer_y;
150 finished (event, _had_movement);
152 _editor->hide_verbose_canvas_cursor();
156 return _had_movement;
160 Drag::adjusted_current_frame (GdkEvent* event) const
164 if (_current_pointer_frame > _pointer_frame_offset) {
165 pos = _current_pointer_frame - _pointer_frame_offset;
168 _editor->snap_to_with_modifier (pos, event);
174 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
176 _last_pointer_x = _current_pointer_x;
177 _last_pointer_y = _current_pointer_y;
178 _current_pointer_frame = _editor->event_frame (event, &_current_pointer_x, &_current_pointer_y);
180 if (!from_autoscroll && !_move_threshold_passed) {
182 bool const xp = (::llabs ((nframes64_t) (_current_pointer_x - _grab_x)) > 4LL);
183 bool const yp = (::llabs ((nframes64_t) (_current_pointer_y - _grab_y)) > 4LL);
185 _move_threshold_passed = (xp || yp);
187 if (apply_move_threshold() && _move_threshold_passed) {
189 _grab_frame = _current_pointer_frame;
190 _grab_x = _current_pointer_x;
191 _grab_y = _current_pointer_y;
192 _last_pointer_frame = _grab_frame;
193 _pointer_frame_offset = _grab_frame - _last_frame_position;
198 bool old_had_movement = _had_movement;
200 /* a motion event has happened, so we've had movement... */
201 _had_movement = true;
203 /* ... unless we're using a move threshold and we've not yet passed it */
204 if (apply_move_threshold() && !_move_threshold_passed) {
205 _had_movement = false;
208 if (active (_editor->mouse_mode)) {
210 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
211 if (!from_autoscroll) {
212 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
215 motion (event, _had_movement != old_had_movement);
227 _editor->stop_canvas_autoscroll ();
228 _editor->hide_verbose_canvas_cursor ();
233 /* put it back where it came from */
238 _item->i2w (cxw, cyw);
239 _item->move (_original_x - cxw, _original_y - cyw);
244 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
249 RegionView::RegionViewGoingAway.connect (mem_fun (*this, &RegionDrag::region_going_away));
253 RegionDrag::region_going_away (RegionView* v)
258 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
259 : RegionDrag (e, i, p, v),
269 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
271 Drag::start_grab (event);
273 _editor->show_verbose_time_cursor (_last_frame_position, 10);
276 RegionMotionDrag::TimeAxisViewSummary
277 RegionMotionDrag::get_time_axis_view_summary ()
279 int32_t children = 0;
280 TimeAxisViewSummary sum;
282 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
284 /* get a bitmask representing the visible tracks */
286 for (Editor::TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
287 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
288 TimeAxisView::Children children_list;
290 /* zeroes are audio/MIDI tracks. ones are other types. */
292 if (!rtv->hidden()) {
294 if (!rtv->is_track()) {
295 /* not an audio nor MIDI track */
296 sum.tracks = sum.tracks |= (0x01 << rtv->order());
299 sum.height_list[rtv->order()] = (*i)->current_height();
302 if ((children_list = rtv->get_child_list()).size() > 0) {
303 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
304 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
305 sum.height_list[rtv->order() + children] = (*j)->current_height();
316 RegionMotionDrag::compute_y_delta (
317 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
318 int32_t last_pointer_layer, int32_t current_pointer_layer,
319 TimeAxisViewSummary const & tavs,
320 int32_t* pointer_order_span, int32_t* pointer_layer_span,
321 int32_t* canvas_pointer_order_span
325 *pointer_order_span = 0;
326 *pointer_layer_span = 0;
330 bool clamp_y_axis = false;
332 /* the change in track order between this callback and the last */
333 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
334 /* the change in layer between this callback and the last;
335 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
336 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
338 if (*pointer_order_span != 0) {
340 /* find the actual pointer span, in terms of the number of visible tracks;
341 to do this, we reduce |pointer_order_span| by the number of hidden tracks
344 *canvas_pointer_order_span = *pointer_order_span;
345 if (last_pointer_view->order() >= current_pointer_view->order()) {
346 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
347 if (tavs.height_list[y] == 0) {
348 *canvas_pointer_order_span--;
352 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
353 if (tavs.height_list[y] == 0) {
354 *canvas_pointer_order_span++;
359 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
361 RegionView* rv = (*i);
363 if (rv->region()->locked()) {
367 double ix1, ix2, iy1, iy2;
368 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
369 rv->get_canvas_frame()->i2w (ix1, iy1);
370 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
372 /* get the new trackview for this particular region */
373 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
375 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
377 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
378 as surely this is a per-region thing... */
380 clamp_y_axis = y_movement_disallowed (
381 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
389 } else if (_dest_trackview == current_pointer_view) {
391 if (current_pointer_layer == last_pointer_layer) {
392 /* No movement; clamp */
398 _dest_trackview = current_pointer_view;
399 _dest_layer = current_pointer_layer;
407 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
409 *pending_region_position = 0;
411 /* compute the amount of pointer motion in frames, and where
412 the region would be if we moved it by that much.
414 if (_current_pointer_frame >= _pointer_frame_offset) {
416 nframes64_t sync_frame;
417 nframes64_t sync_offset;
420 *pending_region_position = _current_pointer_frame - _pointer_frame_offset;
422 sync_offset = _primary->region()->sync_offset (sync_dir);
424 /* we don't handle a sync point that lies before zero.
426 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
428 sync_frame = *pending_region_position + (sync_dir*sync_offset);
430 _editor->snap_to_with_modifier (sync_frame, event);
432 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
435 *pending_region_position = _last_frame_position;
440 if (*pending_region_position > max_frames - _primary->region()->length()) {
441 *pending_region_position = _last_frame_position;
446 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
448 /* now compute the canvas unit distance we need to move the regionview
449 to make it appear at the new location.
452 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
454 if (*pending_region_position <= _last_frame_position) {
456 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
458 RegionView* rv = (*i);
460 // If any regionview is at zero, we need to know so we can stop further leftward motion.
462 double ix1, ix2, iy1, iy2;
463 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
464 rv->get_canvas_frame()->i2w (ix1, iy1);
466 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
468 *pending_region_position = _last_frame_position;
475 _last_frame_position = *pending_region_position;
482 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
486 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
488 vector<int32_t>::iterator j;
490 /* *pointer* variables reflect things about the pointer; as we may be moving
491 multiple regions, much detail must be computed per-region */
493 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
494 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
495 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
496 is always 0 regardless of what the region's "real" layer is */
497 RouteTimeAxisView* current_pointer_view;
498 layer_t current_pointer_layer;
499 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
503 /* TimeAxisView that we were pointing at last time we entered this method */
504 TimeAxisView const * const last_pointer_view = _dest_trackview;
505 /* the order of the track that we were pointing at last time we entered this method */
506 int32_t const last_pointer_order = last_pointer_view->order ();
507 /* the layer that we were pointing at last time we entered this method */
508 layer_t const last_pointer_layer = _dest_layer;
510 int32_t pointer_order_span;
511 int32_t pointer_layer_span;
512 int32_t canvas_pointer_order_span;
514 bool const clamp_y_axis = compute_y_delta (
515 last_pointer_view, current_pointer_view,
516 last_pointer_layer, current_pointer_layer, tavs,
517 &pointer_order_span, &pointer_layer_span,
518 &canvas_pointer_order_span
521 nframes64_t pending_region_position;
522 double const x_delta = compute_x_delta (event, &pending_region_position);
524 /*************************************************************
526 ************************************************************/
528 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0) {
529 /* haven't reached next snap point, and we're not switching
530 trackviews nor layers. nothing to do.
535 /*************************************************************
537 ************************************************************/
539 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
541 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
543 RegionView* rv = (*i);
545 if (rv->region()->locked()) {
549 /* here we are calculating the y distance from the
550 top of the first track view to the top of the region
551 area of the track view that we're working on */
553 /* this x value is just a dummy value so that we have something
558 /* distance from the top of this track view to the region area
559 of our track view is always 1 */
563 /* convert to world coordinates, ie distance from the top of
566 rv->get_canvas_frame()->i2w (ix1, iy1);
568 /* compensate for the ruler section and the vertical scrollbar position */
569 iy1 += _editor->get_trackview_group_vertical_offset ();
573 // hide any dependent views
575 rv->get_time_axis_view().hide_dependent_views (*rv);
578 reparent to a non scrolling group so that we can keep the
579 region selection above all time axis views.
580 reparenting means we have to move the rv as the two
581 parent groups have different coordinates.
584 rv->get_canvas_group()->property_y() = iy1 - 1;
585 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
587 rv->fake_set_opaque (true);
590 /* current view for this particular region */
591 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
592 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
594 if (pointer_order_span != 0 && !clamp_y_axis) {
596 /* INTER-TRACK MOVEMENT */
598 /* move through the height list to the track that the region is currently on */
599 vector<int32_t>::iterator j = tavs.height_list.begin ();
601 while (j != tavs.height_list.end () && x != rtv->order ()) {
607 int32_t temp_pointer_order_span = canvas_pointer_order_span;
609 if (j != tavs.height_list.end ()) {
611 /* Account for layers in the original and
612 destination tracks. If we're moving around in layers we assume
613 that only one track is involved, so it's ok to use *pointer*
616 StreamView* lv = last_pointer_view->view ();
619 /* move to the top of the last trackview */
620 if (lv->layer_display () == Stacked) {
621 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
624 StreamView* cv = current_pointer_view->view ();
627 /* move to the right layer on the current trackview */
628 if (cv->layer_display () == Stacked) {
629 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
632 /* And for being on a non-topmost layer on the new
635 while (temp_pointer_order_span > 0) {
636 /* we're moving up canvas-wise,
637 so we need to find the next track height
639 if (j != tavs.height_list.begin()) {
643 if (x != last_pointer_order) {
645 ++temp_pointer_order_span;
650 temp_pointer_order_span--;
653 while (temp_pointer_order_span < 0) {
657 if (x != last_pointer_order) {
659 --temp_pointer_order_span;
663 if (j != tavs.height_list.end()) {
667 temp_pointer_order_span++;
671 /* find out where we'll be when we move and set height accordingly */
673 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
674 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
675 rv->set_height (temp_rtv->view()->child_height());
677 /* if you un-comment the following, the region colours will follow
678 the track colours whilst dragging; personally
679 i think this can confuse things, but never mind.
682 //const GdkColor& col (temp_rtv->view->get_region_color());
683 //rv->set_color (const_cast<GdkColor&>(col));
687 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
689 /* INTER-LAYER MOVEMENT in the same track */
690 y_delta = rtv->view()->child_height () * pointer_layer_span;
695 _editor->mouse_brush_insert_region (rv, pending_region_position);
697 rv->move (x_delta, y_delta);
700 } /* foreach region */
703 _editor->cursor_group->raise_to_top();
706 if (x_delta != 0 && !_brushing) {
707 _editor->show_verbose_time_cursor (_last_frame_position, 10);
712 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
714 if (_copy && first_move) {
715 copy_regions (event);
718 RegionMotionDrag::motion (event, first_move);
722 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
724 vector<RegionView*> copies;
725 boost::shared_ptr<Diskstream> ds;
726 boost::shared_ptr<Playlist> from_playlist;
727 RegionSelection new_views;
728 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
729 PlaylistSet modified_playlists;
730 PlaylistSet frozen_playlists;
731 list <sigc::connection> modified_playlist_connections;
732 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
733 nframes64_t drag_delta;
734 bool changed_tracks, changed_position;
735 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
736 RouteTimeAxisView* source_tv;
738 if (!movement_occurred) {
743 if (Config->get_edit_mode() == Splice && !_editor->pre_drag_region_selection.empty()) {
744 _editor->selection->set (_editor->pre_drag_region_selection);
745 _editor->pre_drag_region_selection.clear ();
749 /* all changes were made during motion event handlers */
752 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
753 copies.push_back (*i);
760 /* reverse this here so that we have the correct logic to finalize
764 if (Config->get_edit_mode() == Lock) {
765 _x_constrained = !_x_constrained;
769 if (_x_constrained) {
770 _editor->begin_reversible_command (_("fixed time region copy"));
772 _editor->begin_reversible_command (_("region copy"));
775 if (_x_constrained) {
776 _editor->begin_reversible_command (_("fixed time region drag"));
778 _editor->begin_reversible_command (_("region drag"));
782 _have_transaction = true;
784 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
785 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
787 drag_delta = _primary->region()->position() - _last_frame_position;
789 _editor->update_canvas_now ();
791 /* make a list of where each region ended up */
792 final = find_time_axis_views_and_layers ();
794 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
796 RegionView* rv = (*i);
797 RouteTimeAxisView* dest_rtv = final[*i].first;
798 layer_t dest_layer = final[*i].second;
802 if (rv->region()->locked()) {
807 if (changed_position && !_x_constrained) {
808 where = rv->region()->position() - drag_delta;
810 where = rv->region()->position();
813 boost::shared_ptr<Region> new_region;
816 /* we already made a copy */
817 new_region = rv->region();
819 /* undo the previous hide_dependent_views so that xfades don't
820 disappear on copying regions
823 //rv->get_time_axis_view().reveal_dependent_views (*rv);
825 } else if (changed_tracks && dest_rtv->playlist()) {
826 new_region = RegionFactory::create (rv->region());
829 if (changed_tracks || _copy) {
831 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
838 _editor->latest_regionviews.clear ();
840 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (mem_fun(*_editor, &Editor::collect_new_region_view));
842 insert_result = modified_playlists.insert (to_playlist);
844 if (insert_result.second) {
845 _editor->session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
848 to_playlist->add_region (new_region, where);
849 if (dest_rtv->view()->layer_display() == Stacked) {
850 new_region->set_layer (dest_layer);
851 new_region->set_pending_explicit_relayer (true);
856 if (!_editor->latest_regionviews.empty()) {
857 // XXX why just the first one ? we only expect one
858 // commented out in nick_m's canvas reworking. is that intended?
859 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
860 new_views.push_back (_editor->latest_regionviews.front());
865 motion on the same track. plonk the previously reparented region
866 back to its original canvas group (its streamview).
867 No need to do anything for copies as they are fake regions which will be deleted.
870 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
871 rv->get_canvas_group()->property_y() = 0;
873 /* just change the model */
875 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
877 if (dest_rtv->view()->layer_display() == Stacked) {
878 rv->region()->set_layer (dest_layer);
879 rv->region()->set_pending_explicit_relayer (true);
882 insert_result = modified_playlists.insert (playlist);
884 if (insert_result.second) {
885 _editor->session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
887 /* freeze to avoid lots of relayering in the case of a multi-region drag */
888 frozen_insert_result = frozen_playlists.insert(playlist);
890 if (frozen_insert_result.second) {
894 rv->region()->set_position (where, (void*) this);
897 if (changed_tracks && !_copy) {
899 /* get the playlist where this drag started. we can't use rv->region()->playlist()
900 because we may have copied the region and it has not been attached to a playlist.
903 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
904 ds = source_tv->get_diskstream();
905 from_playlist = ds->playlist();
909 assert (from_playlist);
911 /* moved to a different audio track, without copying */
913 /* the region that used to be in the old playlist is not
914 moved to the new one - we use a copy of it. as a result,
915 any existing editor for the region should no longer be
919 rv->hide_region_editor();
920 rv->fake_set_opaque (false);
922 /* remove the region from the old playlist */
924 insert_result = modified_playlists.insert (from_playlist);
926 if (insert_result.second) {
927 _editor->session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
930 from_playlist->remove_region (rv->region());
932 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
933 was selected in all of them, then removing it from a playlist will have removed all
934 trace of it from the selection (i.e. there were N regions selected, we removed 1,
935 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
936 corresponding regionview, and the selection is now empty).
938 this could have invalidated any and all iterators into the region selection.
940 the heuristic we use here is: if the region selection is empty, break out of the loop
941 here. if the region selection is not empty, then restart the loop because we know that
942 we must have removed at least the region(view) we've just been working on as well as any
943 that we processed on previous iterations.
945 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
949 if (_views.empty()) {
960 copies.push_back (rv);
964 _editor->selection->add (new_views);
966 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
971 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
972 _editor->session->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
975 _editor->commit_reversible_command ();
977 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
984 RegionMotionDrag::x_move_allowed () const
986 if (Config->get_edit_mode() == Lock) {
987 /* in locked edit mode, reverse the usual meaning of _x_constrained */
988 return _x_constrained;
991 return !_x_constrained;
995 RegionMotionDrag::copy_regions (GdkEvent* event)
997 /* duplicate the regionview(s) and region(s) */
999 list<RegionView*> new_regionviews;
1001 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1003 RegionView* rv = (*i);
1004 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1005 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1007 const boost::shared_ptr<const Region> original = rv->region();
1008 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1012 boost::shared_ptr<AudioRegion> audioregion_copy
1013 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1014 nrv = new AudioRegionView (*arv, audioregion_copy);
1016 boost::shared_ptr<MidiRegion> midiregion_copy
1017 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1018 nrv = new MidiRegionView (*mrv, midiregion_copy);
1023 nrv->get_canvas_group()->show ();
1024 new_regionviews.push_back (nrv);
1027 if (new_regionviews.empty()) {
1031 /* reflect the fact that we are dragging the copies */
1033 _primary = new_regionviews.front();
1034 _views = new_regionviews;
1036 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1039 sync the canvas to what we think is its current state
1040 without it, the canvas seems to
1041 "forget" to update properly after the upcoming reparent()
1042 ..only if the mouse is in rapid motion at the time of the grab.
1043 something to do with regionview creation raking so long?
1045 _editor->update_canvas_now();
1049 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1051 /* Which trackview is this ? */
1053 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1054 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1055 (*layer) = tvp.second;
1057 if (*tv && (*tv)->layer_display() == Overlaid) {
1061 /* The region motion is only processed if the pointer is over
1065 if (!(*tv) || !(*tv)->is_track()) {
1066 /* To make sure we hide the verbose canvas cursor when the mouse is
1067 not held over and audiotrack.
1069 _editor->hide_verbose_canvas_cursor ();
1076 /** @param new_order New track order.
1077 * @param old_order Old track order.
1078 * @param visible_y_low Lowest visible order.
1079 * @return true if y movement should not happen, otherwise false.
1082 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1084 if (new_order != old_order) {
1086 /* this isn't the pointer track */
1090 /* moving up the canvas */
1091 if ( (new_order - y_span) >= tavs.visible_y_low) {
1095 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1096 int32_t visible_tracks = 0;
1097 while (visible_tracks < y_span ) {
1099 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1100 /* passing through a hidden track */
1105 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1106 /* moving to a non-track; disallow */
1112 /* moving beyond the lowest visible track; disallow */
1116 } else if (y_span < 0) {
1118 /* moving down the canvas */
1119 if ((new_order - y_span) <= tavs.visible_y_high) {
1121 int32_t visible_tracks = 0;
1123 while (visible_tracks > y_span ) {
1126 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1127 /* passing through a hidden track */
1132 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1133 /* moving to a non-track; disallow */
1140 /* moving beyond the highest visible track; disallow */
1147 /* this is the pointer's track */
1149 if ((new_order - y_span) > tavs.visible_y_high) {
1150 /* we will overflow */
1152 } else if ((new_order - y_span) < tavs.visible_y_low) {
1153 /* we will overflow */
1162 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1163 : RegionMotionDrag (e, i, p, v, b),
1166 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1168 _dest_trackview = tv;
1169 if (tv->layer_display() == Overlaid) {
1172 _dest_layer = _primary->region()->layer ();
1176 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1177 if (rtv && rtv->is_track()) {
1178 speed = rtv->get_diskstream()->speed ();
1181 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1185 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1187 RegionMotionDrag::start_grab (event, c);
1189 _pointer_frame_offset = _grab_frame - _last_frame_position;
1192 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1193 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1195 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1196 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1198 _primary = v->view()->create_region_view (r, false, false);
1200 _primary->get_canvas_group()->show ();
1201 _primary->set_position (pos, 0);
1202 _views.push_back (_primary);
1204 _last_frame_position = pos;
1206 _item = _primary->get_canvas_group ();
1207 _dest_trackview = v;
1208 _dest_layer = _primary->region()->layer ();
1211 map<RegionView*, pair<RouteTimeAxisView*, int> >
1212 RegionMotionDrag::find_time_axis_views_and_layers ()
1214 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1216 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1218 double ix1, ix2, iy1, iy2;
1219 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1220 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1221 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1223 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1224 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1232 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1234 _editor->update_canvas_now ();
1236 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1238 RouteTimeAxisView* dest_rtv = final[_primary].first;
1240 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1241 _primary->get_canvas_group()->property_y() = 0;
1243 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1245 _editor->begin_reversible_command (_("insert region"));
1246 XMLNode& before = playlist->get_state ();
1247 playlist->add_region (_primary->region (), _last_frame_position);
1248 _editor->session->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1249 _editor->commit_reversible_command ();
1256 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1257 : RegionMoveDrag (e, i, p, v, false, false)
1262 struct RegionSelectionByPosition {
1263 bool operator() (RegionView*a, RegionView* b) {
1264 return a->region()->position () < b->region()->position();
1269 RegionSpliceDrag::motion (GdkEvent* /*event*/, bool)
1271 RouteTimeAxisView* tv;
1274 if (!check_possible (&tv, &layer)) {
1280 if (_current_pointer_x - _grab_x > 0) {
1286 RegionSelection copy (_editor->selection->regions);
1288 RegionSelectionByPosition cmp;
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 (_current_pointer_frame < (*i)->region()->last_frame() + 1) {
1314 if (_current_pointer_frame > (*i)->region()->first_frame()) {
1320 playlist->shuffle ((*i)->region(), dir);
1322 _grab_x = _current_pointer_x;
1327 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1333 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1341 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1343 _dest_trackview = _view;
1345 Drag::start_grab (event);
1350 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1353 // TODO: create region-create-drag region view here
1356 // TODO: resize region-create-drag region view here
1360 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1362 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1368 if (!movement_occurred) {
1369 mtv->add_region (_grab_frame);
1371 motion (event, false);
1372 // TODO: create region-create-drag region here
1376 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1384 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1387 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1389 Drag::start_grab (event);
1391 region = &cnote->region_view();
1393 double region_start = region->get_position_pixels();
1394 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1396 if (_grab_x <= middle_point) {
1397 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1400 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1404 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1406 if (event->motion.state & Keyboard::PrimaryModifier) {
1412 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1414 if (ms.size() > 1) {
1415 /* has to be relative, may make no sense otherwise */
1419 region->note_selected (cnote, true);
1421 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1422 MidiRegionSelection::iterator next;
1425 (*r)->begin_resizing (at_front);
1431 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1433 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1434 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1435 (*r)->update_resizing (at_front, _current_pointer_x - _grab_x, relative);
1440 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1442 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1443 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1444 (*r)->commit_resizing (at_front, _current_pointer_x - _grab_x, relative);
1449 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1455 RegionGainDrag::finished (GdkEvent *, bool)
1460 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1461 : RegionDrag (e, i, p, v)
1467 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1470 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1471 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1473 if (tv && tv->is_track()) {
1474 speed = tv->get_diskstream()->speed();
1477 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1478 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1479 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1481 Drag::start_grab (event, _editor->trimmer_cursor);
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 (_current_pointer_frame < (region_start + region_length/2)) {
1488 /* closer to start */
1489 _operation = StartTrim;
1490 } else if (_current_pointer_frame > (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 (_current_pointer_frame, 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 if (_last_pointer_frame > _current_pointer_frame) {
1533 left_direction = true;
1535 left_direction = false;
1538 _editor->snap_to_with_modifier (_current_pointer_frame, event);
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 (_current_pointer_frame == _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 - _current_pointer_frame);
1589 frame_delta = (_current_pointer_frame - _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) && (_current_pointer_frame <= 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) && (_current_pointer_frame > (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)
1631 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1637 switch (_operation) {
1639 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1642 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1645 _editor->show_verbose_time_cursor(_current_pointer_frame, 10);
1649 _last_pointer_frame = _current_pointer_frame;
1654 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1656 if (movement_occurred) {
1657 motion (event, false);
1659 if (!_editor->selection->selected (_primary)) {
1660 _editor->thaw_region_after_trim (*_primary);
1663 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1664 _editor->thaw_region_after_trim (**i);
1665 (*i)->fake_set_opaque (true);
1668 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1670 if (_have_transaction) {
1671 _editor->session->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1675 _editor->motion_frozen_playlists.clear ();
1677 if (_have_transaction) {
1678 _editor->commit_reversible_command();
1682 /* no mouse movement */
1683 _editor->point_trim (event);
1687 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1691 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1696 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1699 // create a dummy marker for visual representation of moving the copy.
1700 // The actual copying is not done before we reach the finish callback.
1702 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1703 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1704 *new MeterSection (_marker->meter()));
1706 _item = &new_marker->the_item ();
1707 _marker = new_marker;
1711 MetricSection& section (_marker->meter());
1713 if (!section.movable()) {
1719 Drag::start_grab (event, cursor);
1721 _pointer_frame_offset = _grab_frame - _marker->meter().frame();
1723 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1727 MeterMarkerDrag::motion (GdkEvent* event, bool)
1729 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1731 if (adjusted_frame == _last_pointer_frame) {
1735 _marker->set_position (adjusted_frame);
1737 _last_pointer_frame = adjusted_frame;
1739 _editor->show_verbose_time_cursor (adjusted_frame, 10);
1743 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1745 if (!movement_occurred) {
1749 motion (event, false);
1753 TempoMap& map (_editor->session->tempo_map());
1754 map.bbt_time (_last_pointer_frame, when);
1756 if (_copy == true) {
1757 _editor->begin_reversible_command (_("copy meter mark"));
1758 XMLNode &before = map.get_state();
1759 map.add_meter (_marker->meter(), when);
1760 XMLNode &after = map.get_state();
1761 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1762 _editor->commit_reversible_command ();
1764 // delete the dummy marker we used for visual representation of copying.
1765 // a new visual marker will show up automatically.
1768 _editor->begin_reversible_command (_("move meter mark"));
1769 XMLNode &before = map.get_state();
1770 map.move_meter (_marker->meter(), when);
1771 XMLNode &after = map.get_state();
1772 _editor->session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1773 _editor->commit_reversible_command ();
1777 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1781 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1786 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1791 // create a dummy marker for visual representation of moving the copy.
1792 // The actual copying is not done before we reach the finish callback.
1794 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1795 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1796 *new TempoSection (_marker->tempo()));
1798 _item = &new_marker->the_item ();
1799 _marker = new_marker;
1803 MetricSection& section (_marker->tempo());
1805 if (!section.movable()) {
1810 Drag::start_grab (event, cursor);
1812 _pointer_frame_offset = _grab_frame - _marker->tempo().frame();
1813 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
1817 TempoMarkerDrag::motion (GdkEvent* event, bool)
1819 nframes64_t const adjusted_frame = adjusted_current_frame (event);
1821 if (adjusted_frame == _last_pointer_frame) {
1825 /* OK, we've moved far enough to make it worth actually move the thing. */
1827 _marker->set_position (adjusted_frame);
1829 _editor->show_verbose_time_cursor (adjusted_frame, 10);
1831 _last_pointer_frame = adjusted_frame;
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);
1925 _last_pointer_frame = adjusted_frame;
1929 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1931 _editor->_dragging_playhead = false;
1933 if (!movement_occurred && _stop) {
1937 motion (event, false);
1939 if (_item == &_editor->playhead_cursor->canvas_item) {
1940 if (_editor->session) {
1941 _editor->session->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1942 _editor->_pending_locate_request = true;
1947 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1948 : RegionDrag (e, i, p, v)
1954 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1956 Drag::start_grab (event, cursor);
1958 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1959 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1961 _pointer_frame_offset = _grab_frame - ((nframes64_t) r->fade_in()->back()->when + r->position());
1962 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1966 FadeInDrag::motion (GdkEvent* event, bool)
1968 nframes64_t fade_length;
1970 nframes64_t const pos = adjusted_current_frame (event);
1972 boost::shared_ptr<Region> region = _primary->region ();
1974 if (pos < (region->position() + 64)) {
1975 fade_length = 64; // this should be a minimum defined somewhere
1976 } else if (pos > region->last_frame()) {
1977 fade_length = region->length();
1979 fade_length = pos - region->position();
1982 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1984 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1990 tmp->reset_fade_in_shape_width (fade_length);
1993 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
1997 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
1999 if (!movement_occurred) {
2003 nframes64_t fade_length;
2005 nframes64_t const pos = adjusted_current_frame (event);
2007 boost::shared_ptr<Region> region = _primary->region ();
2009 if (pos < (region->position() + 64)) {
2010 fade_length = 64; // this should be a minimum defined somewhere
2011 } else if (pos > region->last_frame()) {
2012 fade_length = region->length();
2014 fade_length = pos - region->position();
2017 _editor->begin_reversible_command (_("change fade in length"));
2019 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2021 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2027 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2028 XMLNode &before = alist->get_state();
2030 tmp->audio_region()->set_fade_in_length (fade_length);
2031 tmp->audio_region()->set_fade_in_active (true);
2033 XMLNode &after = alist->get_state();
2034 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2037 _editor->commit_reversible_command ();
2040 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2041 : RegionDrag (e, i, p, v)
2047 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2049 Drag::start_grab (event, cursor);
2051 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2052 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2054 _pointer_frame_offset = _grab_frame - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2055 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2059 FadeOutDrag::motion (GdkEvent* event, bool)
2061 nframes64_t fade_length;
2063 nframes64_t const pos = adjusted_current_frame (event);
2065 boost::shared_ptr<Region> region = _primary->region ();
2067 if (pos > (region->last_frame() - 64)) {
2068 fade_length = 64; // this should really be a minimum fade defined somewhere
2070 else if (pos < region->position()) {
2071 fade_length = region->length();
2074 fade_length = region->last_frame() - pos;
2077 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2079 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2085 tmp->reset_fade_out_shape_width (fade_length);
2088 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2092 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2094 if (!movement_occurred) {
2098 nframes64_t fade_length;
2100 nframes64_t const pos = adjusted_current_frame (event);
2102 boost::shared_ptr<Region> region = _primary->region ();
2104 if (pos > (region->last_frame() - 64)) {
2105 fade_length = 64; // this should really be a minimum fade defined somewhere
2107 else if (pos < region->position()) {
2108 fade_length = region->length();
2111 fade_length = region->last_frame() - pos;
2114 _editor->begin_reversible_command (_("change fade out length"));
2116 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2118 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2124 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2125 XMLNode &before = alist->get_state();
2127 tmp->audio_region()->set_fade_out_length (fade_length);
2128 tmp->audio_region()->set_fade_out_active (true);
2130 XMLNode &after = alist->get_state();
2131 _editor->session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2134 _editor->commit_reversible_command ();
2137 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2140 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2143 _points.push_back (Gnome::Art::Point (0, 0));
2144 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2146 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2147 _line->property_width_pixels() = 1;
2148 _line->property_points () = _points;
2151 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2154 MarkerDrag::~MarkerDrag ()
2156 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2162 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2164 Drag::start_grab (event, cursor);
2168 Location *location = _editor->find_location_from_marker (_marker, is_start);
2169 _editor->_dragging_edit_point = true;
2171 _pointer_frame_offset = _grab_frame - (is_start ? location->start() : location->end());
2173 update_item (location);
2175 // _drag_line->show();
2176 // _line->raise_to_top();
2179 _editor->show_verbose_time_cursor (location->start(), 10);
2181 _editor->show_verbose_time_cursor (location->end(), 10);
2184 Selection::Operation op = Keyboard::selection_type (event->button.state);
2187 case Selection::Toggle:
2188 _editor->selection->toggle (_marker);
2190 case Selection::Set:
2191 if (!_editor->selection->selected (_marker)) {
2192 _editor->selection->set (_marker);
2195 case Selection::Extend:
2197 Locations::LocationList ll;
2198 list<Marker*> to_add;
2200 _editor->selection->markers.range (s, e);
2201 s = min (_marker->position(), s);
2202 e = max (_marker->position(), e);
2205 if (e < max_frames) {
2208 _editor->session->locations()->find_all_between (s, e, ll, Location::Flags (0));
2209 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2210 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2213 to_add.push_back (lm->start);
2216 to_add.push_back (lm->end);
2220 if (!to_add.empty()) {
2221 _editor->selection->add (to_add);
2225 case Selection::Add:
2226 _editor->selection->add (_marker);
2230 /* set up copies for us to manipulate during the drag */
2232 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2233 Location *l = _editor->find_location_from_marker (*i, is_start);
2234 _copied_locations.push_back (new Location (*l));
2239 MarkerDrag::motion (GdkEvent* event, bool)
2241 nframes64_t f_delta = 0;
2243 bool move_both = false;
2245 Location *real_location;
2246 Location *copy_location = 0;
2248 nframes64_t const newframe = adjusted_current_frame (event);
2250 nframes64_t next = newframe;
2252 if (_current_pointer_frame == _last_pointer_frame) {
2256 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2260 MarkerSelection::iterator i;
2261 list<Location*>::iterator x;
2263 /* find the marker we're dragging, and compute the delta */
2265 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2266 x != _copied_locations.end() && i != _editor->selection->markers.end();
2272 if (marker == _marker) {
2274 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2279 if (real_location->is_mark()) {
2280 f_delta = newframe - copy_location->start();
2284 switch (marker->type()) {
2286 case Marker::LoopStart:
2287 case Marker::PunchIn:
2288 f_delta = newframe - copy_location->start();
2292 case Marker::LoopEnd:
2293 case Marker::PunchOut:
2294 f_delta = newframe - copy_location->end();
2297 /* what kind of marker is this ? */
2305 if (i == _editor->selection->markers.end()) {
2306 /* hmm, impossible - we didn't find the dragged marker */
2310 /* now move them all */
2312 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2313 x != _copied_locations.end() && i != _editor->selection->markers.end();
2319 /* call this to find out if its the start or end */
2321 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2325 if (real_location->locked()) {
2329 if (copy_location->is_mark()) {
2333 copy_location->set_start (copy_location->start() + f_delta);
2337 nframes64_t new_start = copy_location->start() + f_delta;
2338 nframes64_t new_end = copy_location->end() + f_delta;
2340 if (is_start) { // start-of-range marker
2343 copy_location->set_start (new_start);
2344 copy_location->set_end (new_end);
2345 } else if (new_start < copy_location->end()) {
2346 copy_location->set_start (new_start);
2348 _editor->snap_to (next, 1, true);
2349 copy_location->set_end (next);
2350 copy_location->set_start (newframe);
2353 } else { // end marker
2356 copy_location->set_end (new_end);
2357 copy_location->set_start (new_start);
2358 } else if (new_end > copy_location->start()) {
2359 copy_location->set_end (new_end);
2360 } else if (newframe > 0) {
2361 _editor->snap_to (next, -1, true);
2362 copy_location->set_start (next);
2363 copy_location->set_end (newframe);
2368 update_item (copy_location);
2370 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2373 lm->set_position (copy_location->start(), copy_location->end());
2377 _last_pointer_frame = _current_pointer_frame;
2379 assert (!_copied_locations.empty());
2381 _editor->show_verbose_time_cursor (newframe, 10);
2384 _editor->update_canvas_now ();
2389 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2391 if (!movement_occurred) {
2393 /* just a click, do nothing but finish
2394 off the selection process
2397 Selection::Operation op = Keyboard::selection_type (event->button.state);
2400 case Selection::Set:
2401 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2402 _editor->selection->set (_marker);
2406 case Selection::Toggle:
2407 case Selection::Extend:
2408 case Selection::Add:
2415 _editor->_dragging_edit_point = false;
2417 _editor->begin_reversible_command ( _("move marker") );
2418 XMLNode &before = _editor->session->locations()->get_state();
2420 MarkerSelection::iterator i;
2421 list<Location*>::iterator x;
2424 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2425 x != _copied_locations.end() && i != _editor->selection->markers.end();
2428 Location * location = _editor->find_location_from_marker (*i, is_start);
2432 if (location->locked()) {
2436 if (location->is_mark()) {
2437 location->set_start ((*x)->start());
2439 location->set ((*x)->start(), (*x)->end());
2444 XMLNode &after = _editor->session->locations()->get_state();
2445 _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
2446 _editor->commit_reversible_command ();
2452 MarkerDrag::update_item (Location* location)
2454 double const x1 = _editor->frame_to_pixel (location->start());
2456 _points.front().set_x(x1);
2457 _points.back().set_x(x1);
2458 _line->property_points() = _points;
2461 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2463 _cumulative_x_drag (0),
2464 _cumulative_y_drag (0)
2466 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2472 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2474 Drag::start_grab (event, _editor->fader_cursor);
2476 // start the grab at the center of the control point so
2477 // the point doesn't 'jump' to the mouse after the first drag
2478 _grab_x = _point->get_x();
2479 _grab_y = _point->get_y();
2481 _point->line().parent_group().i2w (_grab_x, _grab_y);
2482 _editor->track_canvas->w2c (_grab_x, _grab_y, _grab_x, _grab_y);
2484 _grab_frame = _editor->pixel_to_frame (_grab_x);
2486 _point->line().start_drag (_point, _grab_frame, 0);
2488 float fraction = 1.0 - (_point->get_y() / _point->line().height());
2489 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2490 _current_pointer_x + 10, _current_pointer_y + 10);
2492 _editor->show_verbose_canvas_cursor ();
2496 ControlPointDrag::motion (GdkEvent* event, bool)
2498 double dx = _current_pointer_x - _last_pointer_x;
2499 double dy = _current_pointer_y - _last_pointer_y;
2501 if (event->button.state & Keyboard::SecondaryModifier) {
2506 double cx = _grab_x + _cumulative_x_drag + dx;
2507 double cy = _grab_y + _cumulative_y_drag + dy;
2509 // calculate zero crossing point. back off by .01 to stay on the
2510 // positive side of zero
2512 double zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2513 _point->line().parent_group().i2w(_unused, zero_gain_y);
2515 // make sure we hit zero when passing through
2516 if ((cy < zero_gain_y and (cy - dy) > zero_gain_y)
2517 or (cy > zero_gain_y and (cy - dy) < zero_gain_y)) {
2521 if (_x_constrained) {
2524 if (_y_constrained) {
2528 _cumulative_x_drag = cx - _grab_x;
2529 _cumulative_y_drag = cy - _grab_y;
2531 _point->line().parent_group().w2i (cx, cy);
2535 cy = min ((double) _point->line().height(), cy);
2537 //translate cx to frames
2538 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2540 if (!_x_constrained) {
2541 _editor->snap_to_with_modifier (cx_frames, event);
2544 float const fraction = 1.0 - (cy / _point->line().height());
2546 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2548 _point->line().point_drag (*_point, cx_frames, fraction, push);
2550 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2554 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2556 if (!movement_occurred) {
2560 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2561 _editor->reset_point_selection ();
2565 motion (event, false);
2567 _point->line().end_drag (_point);
2571 ControlPointDrag::active (Editing::MouseMode m)
2573 if (m == Editing::MouseGain) {
2574 /* always active in mouse gain */
2578 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2579 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2582 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2585 _cumulative_y_drag (0)
2590 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2592 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2595 _item = &_line->grab_item ();
2597 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2598 origin, and ditto for y.
2601 double cx = event->button.x;
2602 double cy = event->button.y;
2604 _line->parent_group().w2i (cx, cy);
2606 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2608 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
2609 /* no adjacent points */
2613 Drag::start_grab (event, _editor->fader_cursor);
2615 /* store grab start in parent frame */
2620 double fraction = 1.0 - (cy / _line->height());
2622 _line->start_drag (0, _grab_frame, fraction);
2624 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2625 _current_pointer_x + 10, _current_pointer_y + 10);
2627 _editor->show_verbose_canvas_cursor ();
2631 LineDrag::motion (GdkEvent* event, bool)
2633 double dy = _current_pointer_y - _last_pointer_y;
2635 if (event->button.state & Keyboard::SecondaryModifier) {
2639 double cy = _grab_y + _cumulative_y_drag + dy;
2641 _cumulative_y_drag = cy - _grab_y;
2644 cy = min ((double) _line->height(), cy);
2646 double const fraction = 1.0 - (cy / _line->height());
2650 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2656 _line->line_drag (_before, _after, fraction, push);
2658 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2662 LineDrag::finished (GdkEvent* event, bool)
2664 motion (event, false);
2665 _line->end_drag (0);
2669 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2671 Drag::start_grab (event);
2672 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2676 RubberbandSelectDrag::motion (GdkEvent* event, bool first_move)
2683 /* use a bigger drag threshold than the default */
2685 if (abs ((int) (_current_pointer_frame - _grab_frame)) < 8) {
2689 if (Config->get_rubberbanding_snaps_to_grid()) {
2691 _editor->snap_to_with_modifier (_grab_frame, event);
2693 _editor->snap_to_with_modifier (_current_pointer_frame, event);
2696 /* base start and end on initial click position */
2698 if (_current_pointer_frame < _grab_frame) {
2699 start = _current_pointer_frame;
2702 end = _current_pointer_frame;
2703 start = _grab_frame;
2706 if (_current_pointer_y < _grab_y) {
2707 y1 = _current_pointer_y;
2710 y2 = _current_pointer_y;
2715 if (start != end || y1 != y2) {
2717 double x1 = _editor->frame_to_pixel (start);
2718 double x2 = _editor->frame_to_pixel (end);
2720 _editor->rubberband_rect->property_x1() = x1;
2721 _editor->rubberband_rect->property_y1() = y1;
2722 _editor->rubberband_rect->property_x2() = x2;
2723 _editor->rubberband_rect->property_y2() = y2;
2725 _editor->rubberband_rect->show();
2726 _editor->rubberband_rect->raise_to_top();
2728 _last_pointer_frame = _current_pointer_frame;
2730 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2735 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2737 if (movement_occurred) {
2739 motion (event, false);
2742 if (_current_pointer_y < _grab_y) {
2743 y1 = _current_pointer_y;
2746 y2 = _current_pointer_y;
2751 Selection::Operation op = Keyboard::selection_type (event->button.state);
2754 _editor->begin_reversible_command (_("rubberband selection"));
2756 if (_grab_frame < _last_pointer_frame) {
2757 committed = _editor->select_all_within (_grab_frame, _last_pointer_frame - 1, y1, y2, _editor->track_views, op);
2759 committed = _editor->select_all_within (_last_pointer_frame, _grab_frame - 1, y1, y2, _editor->track_views, op);
2763 _editor->commit_reversible_command ();
2767 if (!getenv("ARDOUR_SAE")) {
2768 _editor->selection->clear_tracks();
2770 _editor->selection->clear_regions();
2771 _editor->selection->clear_points ();
2772 _editor->selection->clear_lines ();
2775 _editor->rubberband_rect->hide();
2779 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2781 Drag::start_grab (event);
2783 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2787 TimeFXDrag::motion (GdkEvent* event, bool)
2789 RegionView* rv = _primary;
2791 _editor->snap_to_with_modifier (_current_pointer_frame, event);
2793 if (_current_pointer_frame == _last_pointer_frame) {
2797 if (_current_pointer_frame > rv->region()->position()) {
2798 rv->get_time_axis_view().show_timestretch (rv->region()->position(), _current_pointer_frame);
2801 _last_pointer_frame = _current_pointer_frame;
2803 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2807 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2809 _primary->get_time_axis_view().hide_timestretch ();
2811 if (!movement_occurred) {
2815 if (_last_pointer_frame < _primary->region()->position()) {
2816 /* backwards drag of the left edge - not usable */
2820 nframes64_t newlen = _last_pointer_frame - _primary->region()->position();
2822 float percentage = (double) newlen / (double) _primary->region()->length();
2824 #ifndef USE_RUBBERBAND
2825 // Soundtouch uses percentage / 100 instead of normal (/ 1)
2826 if (_primary->region()->data_type() == DataType::AUDIO) {
2827 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2831 _editor->begin_reversible_command (_("timestretch"));
2833 // XXX how do timeFX on multiple regions ?
2838 if (_editor->time_stretch (rs, percentage) == 0) {
2839 _editor->session->commit_reversible_command ();
2844 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2846 Drag::start_grab (event);
2850 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2856 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2858 if (movement_occurred && _editor->session) {
2859 /* make sure we stop */
2860 _editor->session->request_transport_speed (0.0);
2864 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2873 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2875 nframes64_t start = 0;
2876 nframes64_t end = 0;
2878 if (_editor->session == 0) {
2882 Gdk::Cursor* cursor = 0;
2884 switch (_operation) {
2885 case CreateSelection:
2886 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2891 cursor = _editor->selector_cursor;
2892 Drag::start_grab (event, cursor);
2895 case SelectionStartTrim:
2896 if (_editor->clicked_axisview) {
2897 _editor->clicked_axisview->order_selection_trims (_item, true);
2899 Drag::start_grab (event, cursor);
2900 cursor = _editor->trimmer_cursor;
2901 start = _editor->selection->time[_editor->clicked_selection].start;
2902 _pointer_frame_offset = _grab_frame - start;
2905 case SelectionEndTrim:
2906 if (_editor->clicked_axisview) {
2907 _editor->clicked_axisview->order_selection_trims (_item, false);
2909 Drag::start_grab (event, cursor);
2910 cursor = _editor->trimmer_cursor;
2911 end = _editor->selection->time[_editor->clicked_selection].end;
2912 _pointer_frame_offset = _grab_frame - end;
2916 start = _editor->selection->time[_editor->clicked_selection].start;
2917 Drag::start_grab (event, cursor);
2918 _pointer_frame_offset = _grab_frame - start;
2922 if (_operation == SelectionMove) {
2923 _editor->show_verbose_time_cursor (start, 10);
2925 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
2930 SelectionDrag::motion (GdkEvent* event, bool first_move)
2932 nframes64_t start = 0;
2933 nframes64_t end = 0;
2937 nframes64_t const pending_position = adjusted_current_frame (event);
2939 /* only alter selection if the current frame is
2940 different from the last frame position (adjusted)
2943 if (pending_position == _last_pointer_frame) {
2947 switch (_operation) {
2948 case CreateSelection:
2951 _editor->snap_to (_grab_frame);
2954 if (pending_position < _grab_frame) {
2955 start = pending_position;
2958 end = pending_position;
2959 start = _grab_frame;
2962 /* first drag: Either add to the selection
2963 or create a new selection->
2968 _editor->begin_reversible_command (_("range selection"));
2969 _have_transaction = true;
2972 /* adding to the selection */
2973 _editor->clicked_selection = _editor->selection->add (start, end);
2976 /* new selection-> */
2977 _editor->clicked_selection = _editor->selection->set (_editor->clicked_axisview, start, end);
2982 case SelectionStartTrim:
2985 _editor->begin_reversible_command (_("trim selection start"));
2986 _have_transaction = true;
2989 start = _editor->selection->time[_editor->clicked_selection].start;
2990 end = _editor->selection->time[_editor->clicked_selection].end;
2992 if (pending_position > end) {
2995 start = pending_position;
2999 case SelectionEndTrim:
3002 _editor->begin_reversible_command (_("trim selection end"));
3003 _have_transaction = true;
3006 start = _editor->selection->time[_editor->clicked_selection].start;
3007 end = _editor->selection->time[_editor->clicked_selection].end;
3009 if (pending_position < start) {
3012 end = pending_position;
3020 _editor->begin_reversible_command (_("move selection"));
3021 _have_transaction = true;
3024 start = _editor->selection->time[_editor->clicked_selection].start;
3025 end = _editor->selection->time[_editor->clicked_selection].end;
3027 length = end - start;
3029 start = pending_position;
3030 _editor->snap_to (start);
3032 end = start + length;
3037 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3038 _editor->start_canvas_autoscroll (1, 0);
3042 _editor->selection->replace (_editor->clicked_selection, start, end);
3045 _last_pointer_frame = pending_position;
3047 if (_operation == SelectionMove) {
3048 _editor->show_verbose_time_cursor(start, 10);
3050 _editor->show_verbose_time_cursor(pending_position, 10);
3055 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3057 Session* s = _editor->session;
3059 if (movement_occurred) {
3060 motion (event, false);
3061 /* XXX this is not object-oriented programming at all. ick */
3062 if (_editor->selection->time.consolidate()) {
3063 _editor->selection->TimeChanged ();
3066 if (_have_transaction) {
3067 _editor->commit_reversible_command ();
3070 /* XXX what if its a music time selection? */
3071 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3072 s->request_play_range (&_editor->selection->time, true);
3077 /* just a click, no pointer movement.*/
3079 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3081 _editor->selection->clear_time();
3085 if (s && s->get_play_range () && s->transport_rolling()) {
3086 s->request_stop (false, false);
3091 _editor->stop_canvas_autoscroll ();
3094 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3099 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3100 _drag_rect->hide ();
3102 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3103 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3107 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3109 if (_editor->session == 0) {
3113 Gdk::Cursor* cursor = 0;
3115 if (!_editor->temp_location) {
3116 _editor->temp_location = new Location;
3119 switch (_operation) {
3120 case CreateRangeMarker:
3121 case CreateTransportMarker:
3122 case CreateCDMarker:
3124 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3129 cursor = _editor->selector_cursor;
3133 Drag::start_grab (event, cursor);
3135 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3139 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3141 nframes64_t start = 0;
3142 nframes64_t end = 0;
3143 ArdourCanvas::SimpleRect *crect;
3145 switch (_operation) {
3146 case CreateRangeMarker:
3147 crect = _editor->range_bar_drag_rect;
3149 case CreateTransportMarker:
3150 crect = _editor->transport_bar_drag_rect;
3152 case CreateCDMarker:
3153 crect = _editor->cd_marker_bar_drag_rect;
3156 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3161 _editor->snap_to_with_modifier (_current_pointer_frame, event);
3163 /* only alter selection if the current frame is
3164 different from the last frame position.
3167 if (_current_pointer_frame == _last_pointer_frame) {
3171 switch (_operation) {
3172 case CreateRangeMarker:
3173 case CreateTransportMarker:
3174 case CreateCDMarker:
3176 _editor->snap_to (_grab_frame);
3179 if (_current_pointer_frame < _grab_frame) {
3180 start = _current_pointer_frame;
3183 end = _current_pointer_frame;
3184 start = _grab_frame;
3187 /* first drag: Either add to the selection
3188 or create a new selection.
3193 _editor->temp_location->set (start, end);
3197 update_item (_editor->temp_location);
3199 //_drag_rect->raise_to_top();
3205 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3206 _editor->start_canvas_autoscroll (1, 0);
3210 _editor->temp_location->set (start, end);
3212 double x1 = _editor->frame_to_pixel (start);
3213 double x2 = _editor->frame_to_pixel (end);
3214 crect->property_x1() = x1;
3215 crect->property_x2() = x2;
3217 update_item (_editor->temp_location);
3220 _last_pointer_frame = _current_pointer_frame;
3222 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3227 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3229 Location * newloc = 0;
3233 if (movement_occurred) {
3234 motion (event, false);
3237 switch (_operation) {
3238 case CreateRangeMarker:
3239 case CreateCDMarker:
3241 _editor->begin_reversible_command (_("new range marker"));
3242 XMLNode &before = _editor->session->locations()->get_state();
3243 _editor->session->locations()->next_available_name(rangename,"unnamed");
3244 if (_operation == CreateCDMarker) {
3245 flags = Location::IsRangeMarker | Location::IsCDMarker;
3246 _editor->cd_marker_bar_drag_rect->hide();
3249 flags = Location::IsRangeMarker;
3250 _editor->range_bar_drag_rect->hide();
3252 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3253 _editor->session->locations()->add (newloc, true);
3254 XMLNode &after = _editor->session->locations()->get_state();
3255 _editor->session->add_command(new MementoCommand<Locations>(*(_editor->session->locations()), &before, &after));
3256 _editor->commit_reversible_command ();
3260 case CreateTransportMarker:
3261 // popup menu to pick loop or punch
3262 _editor->new_transport_marker_context_menu (&event->button, _item);
3266 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3268 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3273 _editor->session->locations()->marks_either_side (_grab_frame, start, end);
3275 if (end == max_frames) {
3276 end = _editor->session->current_end_frame ();
3279 if (start == max_frames) {
3280 start = _editor->session->current_start_frame ();
3283 switch (_editor->mouse_mode) {
3285 /* find the two markers on either side and then make the selection from it */
3286 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3290 /* find the two markers on either side of the click and make the range out of it */
3291 _editor->selection->set (0, start, end);
3300 _editor->stop_canvas_autoscroll ();
3306 RangeMarkerBarDrag::update_item (Location* location)
3308 double const x1 = _editor->frame_to_pixel (location->start());
3309 double const x2 = _editor->frame_to_pixel (location->end());
3311 _drag_rect->property_x1() = x1;
3312 _drag_rect->property_x2() = x2;
3316 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3318 Drag::start_grab (event, _editor->zoom_cursor);
3319 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3323 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3328 _editor->snap_to_with_modifier (_current_pointer_frame, event);
3331 _editor->snap_to_with_modifier (_grab_frame, event);
3334 if (_current_pointer_frame == _last_pointer_frame) {
3338 /* base start and end on initial click position */
3339 if (_current_pointer_frame < _grab_frame) {
3340 start = _current_pointer_frame;
3343 end = _current_pointer_frame;
3344 start = _grab_frame;
3350 _editor->zoom_rect->show();
3351 _editor->zoom_rect->raise_to_top();
3354 _editor->reposition_zoom_rect(start, end);
3356 _last_pointer_frame = _current_pointer_frame;
3358 _editor->show_verbose_time_cursor (_current_pointer_frame, 10);
3363 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3365 if (movement_occurred) {
3366 motion (event, false);
3368 if (_grab_frame < _last_pointer_frame) {
3369 _editor->temporal_zoom_by_frame (_grab_frame, _last_pointer_frame, "mouse zoom");
3371 _editor->temporal_zoom_by_frame (_last_pointer_frame, _grab_frame, "mouse zoom");
3374 _editor->temporal_zoom_to_frame (false, _grab_frame);
3376 temporal_zoom_step (false);
3377 center_screen (_grab_frame);
3381 _editor->zoom_rect->hide();
3384 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3387 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3388 region = &cnote->region_view();
3392 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3394 Drag::start_grab (event);
3397 drag_delta_note = 0;
3402 event_x = _current_pointer_x;
3403 event_y = _current_pointer_y;
3405 _item->property_parent().get_value()->w2i(event_x, event_y);
3407 last_x = region->snap_to_pixel(event_x);
3410 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3412 if (!(was_selected = cnote->selected())) {
3414 /* tertiary-click means extend selection - we'll do that on button release,
3415 so don't add it here, because otherwise we make it hard to figure
3416 out the "extend-to" range.
3419 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3422 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3425 region->note_selected (cnote, true);
3427 region->unique_select (cnote);
3434 NoteDrag::motion (GdkEvent*, bool)
3436 MidiStreamView* streamview = region->midi_stream_view();
3440 event_x = _current_pointer_x;
3441 event_y = _current_pointer_y;
3443 _item->property_parent().get_value()->w2i(event_x, event_y);
3445 event_x = region->snap_to_pixel(event_x);
3447 double dx = event_x - last_x;
3448 double dy = event_y - last_y;
3453 // Snap to note rows
3455 if (abs (dy) < streamview->note_height()) {
3458 int8_t this_delta_note;
3460 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3462 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3464 drag_delta_note -= this_delta_note;
3465 dy = streamview->note_height() * this_delta_note;
3466 last_y = last_y + dy;
3470 region->move_selection (dx, dy);
3472 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3474 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3475 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3476 _editor->show_verbose_canvas_cursor_with (buf);
3481 NoteDrag::finished (GdkEvent* ev, bool moved)
3483 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3486 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3489 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3491 region->note_deselected (cnote);
3494 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3495 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3497 if (!extend && !add && region->selection_size() > 1) {
3498 region->unique_select(cnote);
3499 } else if (extend) {
3500 region->note_selected (cnote, true, true);
3502 /* it was added during button press */
3507 region->note_dropped (cnote, drag_delta_x, drag_delta_note);