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 #define __STDC_LIMIT_MACROS 1
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
31 #include "audio_region_view.h"
32 #include "midi_region_view.h"
33 #include "ardour_ui.h"
34 #include "gui_thread.h"
35 #include "control_point.h"
37 #include "region_gain_line.h"
38 #include "editor_drag.h"
39 #include "audio_time_axis.h"
40 #include "midi_time_axis.h"
41 #include "canvas-note.h"
42 #include "selection.h"
43 #include "midi_selection.h"
44 #include "automation_time_axis.h"
47 using namespace ARDOUR;
50 using namespace Editing;
51 using namespace ArdourCanvas;
53 using Gtkmm2ext::Keyboard;
55 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
57 DragManager::DragManager (Editor* e)
60 , _current_pointer_frame (0)
65 DragManager::~DragManager ()
70 /** Call abort for each active drag */
76 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
87 DragManager::add (Drag* d)
89 d->set_manager (this);
94 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
96 assert (_drags.empty ());
97 d->set_manager (this);
103 DragManager::start_grab (GdkEvent* e)
105 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
107 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 (*i)->start_grab (e);
112 /** Call end_grab for each active drag.
113 * @return true if any drag reported movement having occurred.
116 DragManager::end_grab (GdkEvent* e)
121 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
122 bool const t = (*i)->end_grab (e);
137 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
141 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->motion_handler (e, from_autoscroll);
155 DragManager::have_item (ArdourCanvas::Item* i) const
157 list<Drag*>::const_iterator j = _drags.begin ();
158 while (j != _drags.end() && (*j)->item () != i) {
162 return j != _drags.end ();
165 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
168 , _pointer_frame_offset (0)
169 , _move_threshold_passed (false)
171 , _last_pointer_frame (0)
177 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
183 cursor = _editor->which_grabber_cursor ();
186 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
190 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
193 cursor = _editor->which_grabber_cursor ();
196 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
198 if (Keyboard::is_button2_event (&event->button)) {
199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
200 _y_constrained = true;
201 _x_constrained = false;
203 _y_constrained = false;
204 _x_constrained = true;
207 _x_constrained = false;
208 _y_constrained = false;
211 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
212 _grab_frame = adjusted_frame (_grab_frame, event);
213 _last_pointer_frame = _grab_frame;
214 _last_pointer_x = _grab_x;
215 _last_pointer_y = _grab_y;
217 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
221 if (_editor->session() && _editor->session()->transport_rolling()) {
224 _was_rolling = false;
227 switch (_editor->snap_type()) {
228 case SnapToRegionStart:
229 case SnapToRegionEnd:
230 case SnapToRegionSync:
231 case SnapToRegionBoundary:
232 _editor->build_region_boundary_cache ();
239 /** Call to end a drag `successfully'. Ungrabs item and calls
240 * subclass' finished() method.
242 * @param event GDK event, or 0.
243 * @return true if some movement occurred, otherwise false.
246 Drag::end_grab (GdkEvent* event)
248 _editor->stop_canvas_autoscroll ();
250 _item->ungrab (event ? event->button.time : 0);
252 finished (event, _move_threshold_passed);
254 _editor->hide_verbose_canvas_cursor();
256 return _move_threshold_passed;
260 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
264 if (f > _pointer_frame_offset) {
265 pos = f - _pointer_frame_offset;
269 _editor->snap_to_with_modifier (pos, event);
276 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
278 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
282 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
284 /* check to see if we have moved in any way that matters since the last motion event */
285 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
286 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
290 pair<nframes64_t, int> const threshold = move_threshold ();
292 bool const old_move_threshold_passed = _move_threshold_passed;
294 if (!from_autoscroll && !_move_threshold_passed) {
296 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
297 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
299 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
302 if (active (_editor->mouse_mode) && _move_threshold_passed) {
304 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
305 if (!from_autoscroll) {
306 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
309 motion (event, _move_threshold_passed != old_move_threshold_passed);
311 _last_pointer_x = _drags->current_pointer_x ();
312 _last_pointer_y = _drags->current_pointer_y ();
313 _last_pointer_frame = adjusted_current_frame (event);
321 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
331 _editor->stop_canvas_autoscroll ();
332 _editor->hide_verbose_canvas_cursor ();
335 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
339 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
340 _views.push_back (DraggingView (*i));
343 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
347 RegionDrag::region_going_away (RegionView* v)
349 list<DraggingView>::iterator i = _views.begin ();
350 while (i != _views.end() && i->view != v) {
354 if (i != _views.end()) {
359 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
360 : RegionDrag (e, i, p, v),
371 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
373 Drag::start_grab (event);
375 _editor->show_verbose_time_cursor (_last_frame_position, 10);
378 RegionMotionDrag::TimeAxisViewSummary
379 RegionMotionDrag::get_time_axis_view_summary ()
381 int32_t children = 0;
382 TimeAxisViewSummary sum;
384 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
386 /* get a bitmask representing the visible tracks */
388 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
389 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
390 TimeAxisView::Children children_list;
392 /* zeroes are audio/MIDI tracks. ones are other types. */
394 if (!rtv->hidden()) {
396 if (!rtv->is_track()) {
397 /* not an audio nor MIDI track */
398 sum.tracks = sum.tracks |= (0x01 << rtv->order());
401 sum.height_list[rtv->order()] = (*i)->current_height();
404 if ((children_list = rtv->get_child_list()).size() > 0) {
405 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
406 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
407 sum.height_list[rtv->order() + children] = (*j)->current_height();
418 RegionMotionDrag::compute_y_delta (
419 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
420 int32_t last_pointer_layer, int32_t current_pointer_layer,
421 TimeAxisViewSummary const & tavs,
422 int32_t* pointer_order_span, int32_t* pointer_layer_span,
423 int32_t* canvas_pointer_order_span
427 *pointer_order_span = 0;
428 *pointer_layer_span = 0;
432 bool clamp_y_axis = false;
434 /* the change in track order between this callback and the last */
435 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
436 /* the change in layer between this callback and the last;
437 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
438 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
440 if (*pointer_order_span != 0) {
442 /* find the actual pointer span, in terms of the number of visible tracks;
443 to do this, we reduce |pointer_order_span| by the number of hidden tracks
446 *canvas_pointer_order_span = *pointer_order_span;
447 if (last_pointer_view->order() >= current_pointer_view->order()) {
448 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
449 if (tavs.height_list[y] == 0) {
450 *canvas_pointer_order_span--;
454 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
455 if (tavs.height_list[y] == 0) {
456 *canvas_pointer_order_span++;
461 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
463 RegionView* rv = i->view;
465 if (rv->region()->locked()) {
469 double ix1, ix2, iy1, iy2;
470 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
471 rv->get_canvas_frame()->i2w (ix1, iy1);
472 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
474 /* get the new trackview for this particular region */
475 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
477 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
479 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
480 as surely this is a per-region thing... */
482 clamp_y_axis = y_movement_disallowed (
483 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
491 } else if (_dest_trackview == current_pointer_view) {
493 if (current_pointer_layer == last_pointer_layer) {
494 /* No movement; clamp */
500 _dest_trackview = current_pointer_view;
501 _dest_layer = current_pointer_layer;
509 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
511 /* compute the amount of pointer motion in frames, and where
512 the region would be if we moved it by that much.
514 *pending_region_position = adjusted_current_frame (event);
516 nframes64_t sync_frame;
517 nframes64_t sync_offset;
520 sync_offset = _primary->region()->sync_offset (sync_dir);
522 /* we don't handle a sync point that lies before zero.
524 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
526 sync_frame = *pending_region_position + (sync_dir*sync_offset);
528 _editor->snap_to_with_modifier (sync_frame, event);
530 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
533 *pending_region_position = _last_frame_position;
536 if (*pending_region_position > max_frames - _primary->region()->length()) {
537 *pending_region_position = _last_frame_position;
542 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
544 /* now compute the canvas unit distance we need to move the regionview
545 to make it appear at the new location.
548 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
550 if (*pending_region_position <= _last_frame_position) {
552 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
554 RegionView* rv = i->view;
556 // If any regionview is at zero, we need to know so we can stop further leftward motion.
558 double ix1, ix2, iy1, iy2;
559 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
560 rv->get_canvas_frame()->i2w (ix1, iy1);
562 if (-x_delta > ix1 + _editor->horizontal_position()) {
564 *pending_region_position = _last_frame_position;
571 _last_frame_position = *pending_region_position;
578 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
582 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
584 vector<int32_t>::iterator j;
586 /* *pointer* variables reflect things about the pointer; as we may be moving
587 multiple regions, much detail must be computed per-region */
589 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
590 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
591 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
592 is always 0 regardless of what the region's "real" layer is */
593 RouteTimeAxisView* current_pointer_view;
594 layer_t current_pointer_layer;
595 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
599 /* TimeAxisView that we were pointing at last time we entered this method */
600 TimeAxisView const * const last_pointer_view = _dest_trackview;
601 /* the order of the track that we were pointing at last time we entered this method */
602 int32_t const last_pointer_order = last_pointer_view->order ();
603 /* the layer that we were pointing at last time we entered this method */
604 layer_t const last_pointer_layer = _dest_layer;
606 int32_t pointer_order_span;
607 int32_t pointer_layer_span;
608 int32_t canvas_pointer_order_span;
610 bool const clamp_y_axis = compute_y_delta (
611 last_pointer_view, current_pointer_view,
612 last_pointer_layer, current_pointer_layer, tavs,
613 &pointer_order_span, &pointer_layer_span,
614 &canvas_pointer_order_span
617 nframes64_t pending_region_position;
618 double const x_delta = compute_x_delta (event, &pending_region_position);
620 /*************************************************************
622 ************************************************************/
624 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
625 /* haven't reached next snap point, and we're not switching
626 trackviews nor layers. nothing to do.
631 /*************************************************************
633 ************************************************************/
635 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
637 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
639 RegionView* rv = i->view;
641 if (rv->region()->locked()) {
645 /* here we are calculating the y distance from the
646 top of the first track view to the top of the region
647 area of the track view that we're working on */
649 /* this x value is just a dummy value so that we have something
654 /* distance from the top of this track view to the region area
655 of our track view is always 1 */
659 /* convert to world coordinates, ie distance from the top of
662 rv->get_canvas_frame()->i2w (ix1, iy1);
664 /* compensate for the ruler section and the vertical scrollbar position */
665 iy1 += _editor->get_trackview_group_vertical_offset ();
669 // hide any dependent views
671 rv->get_time_axis_view().hide_dependent_views (*rv);
674 reparent to a non scrolling group so that we can keep the
675 region selection above all time axis views.
676 reparenting means we have to move the rv as the two
677 parent groups have different coordinates.
680 rv->get_canvas_group()->property_y() = iy1 - 1;
681 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
683 rv->fake_set_opaque (true);
686 /* current view for this particular region */
687 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
688 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
690 if (pointer_order_span != 0 && !clamp_y_axis) {
692 /* INTER-TRACK MOVEMENT */
694 /* move through the height list to the track that the region is currently on */
695 vector<int32_t>::iterator j = tavs.height_list.begin ();
697 while (j != tavs.height_list.end () && x != rtv->order ()) {
703 int32_t temp_pointer_order_span = canvas_pointer_order_span;
705 if (j != tavs.height_list.end ()) {
707 /* Account for layers in the original and
708 destination tracks. If we're moving around in layers we assume
709 that only one track is involved, so it's ok to use *pointer*
712 StreamView* lv = last_pointer_view->view ();
715 /* move to the top of the last trackview */
716 if (lv->layer_display () == Stacked) {
717 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
720 StreamView* cv = current_pointer_view->view ();
723 /* move to the right layer on the current trackview */
724 if (cv->layer_display () == Stacked) {
725 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
728 /* And for being on a non-topmost layer on the new
731 while (temp_pointer_order_span > 0) {
732 /* we're moving up canvas-wise,
733 so we need to find the next track height
735 if (j != tavs.height_list.begin()) {
739 if (x != last_pointer_order) {
741 ++temp_pointer_order_span;
746 temp_pointer_order_span--;
749 while (temp_pointer_order_span < 0) {
753 if (x != last_pointer_order) {
755 --temp_pointer_order_span;
759 if (j != tavs.height_list.end()) {
763 temp_pointer_order_span++;
767 /* find out where we'll be when we move and set height accordingly */
769 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
770 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
771 rv->set_height (temp_rtv->view()->child_height());
773 /* if you un-comment the following, the region colours will follow
774 the track colours whilst dragging; personally
775 i think this can confuse things, but never mind.
778 //const GdkColor& col (temp_rtv->view->get_region_color());
779 //rv->set_color (const_cast<GdkColor&>(col));
783 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
785 /* INTER-LAYER MOVEMENT in the same track */
786 y_delta = rtv->view()->child_height () * pointer_layer_span;
791 _editor->mouse_brush_insert_region (rv, pending_region_position);
793 rv->move (x_delta, y_delta);
796 } /* foreach region */
798 _total_x_delta += x_delta;
801 _editor->cursor_group->raise_to_top();
804 if (x_delta != 0 && !_brushing) {
805 _editor->show_verbose_time_cursor (_last_frame_position, 10);
810 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
812 if (_copy && first_move) {
813 copy_regions (event);
816 RegionMotionDrag::motion (event, first_move);
820 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
822 vector<RegionView*> copies;
823 boost::shared_ptr<Track> tr;
824 boost::shared_ptr<Playlist> from_playlist;
825 boost::shared_ptr<Playlist> to_playlist;
826 RegionSelection new_views;
827 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
828 PlaylistSet modified_playlists;
829 PlaylistSet frozen_playlists;
830 list <sigc::connection> modified_playlist_connections;
831 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
832 nframes64_t drag_delta;
833 bool changed_tracks, changed_position;
834 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
835 RouteTimeAxisView* source_tv;
836 vector<StatefulDiffCommand*> sdc;
838 if (!movement_occurred) {
844 /* all changes were made during motion event handlers */
847 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
848 copies.push_back (i->view);
855 /* reverse this here so that we have the correct logic to finalize
859 if (Config->get_edit_mode() == Lock) {
860 _x_constrained = !_x_constrained;
864 if (_x_constrained) {
865 _editor->begin_reversible_command (_("fixed time region copy"));
867 _editor->begin_reversible_command (_("region copy"));
870 if (_x_constrained) {
871 _editor->begin_reversible_command (_("fixed time region drag"));
873 _editor->begin_reversible_command (_("region drag"));
877 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
878 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
880 drag_delta = _primary->region()->position() - _last_frame_position;
882 _editor->update_canvas_now ();
884 /* make a list of where each region ended up */
885 final = find_time_axis_views_and_layers ();
887 cerr << "Iterate over " << _views.size() << " views\n";
889 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
891 RegionView* rv = i->view;
892 RouteTimeAxisView* dest_rtv = final[rv].first;
893 layer_t dest_layer = final[rv].second;
897 from_playlist.reset ();
898 to_playlist.reset ();
900 if (rv->region()->locked()) {
905 if (changed_position && !_x_constrained) {
906 where = rv->region()->position() - drag_delta;
908 where = rv->region()->position();
911 boost::shared_ptr<Region> new_region;
914 /* we already made a copy */
915 new_region = rv->region();
917 /* undo the previous hide_dependent_views so that xfades don't
918 disappear on copying regions
921 //rv->get_time_axis_view().reveal_dependent_views (*rv);
923 } else if (changed_tracks && dest_rtv->playlist()) {
924 new_region = RegionFactory::create (rv->region());
927 if (changed_tracks || _copy) {
929 to_playlist = dest_rtv->playlist();
936 _editor->latest_regionviews.clear ();
938 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
940 insert_result = modified_playlists.insert (to_playlist);
942 if (insert_result.second) {
943 to_playlist->clear_history ();
946 cerr << "To playlist " << to_playlist->name() << " region history contains "
947 << to_playlist->region_list().change().added.size() << " adds and "
948 << to_playlist->region_list().change().removed.size() << " removes\n";
950 cerr << "Adding new region " << new_region->id() << " based on "
951 << rv->region()->id() << endl;
953 to_playlist->add_region (new_region, where);
955 if (dest_rtv->view()->layer_display() == Stacked) {
956 new_region->set_layer (dest_layer);
957 new_region->set_pending_explicit_relayer (true);
962 if (!_editor->latest_regionviews.empty()) {
963 // XXX why just the first one ? we only expect one
964 // commented out in nick_m's canvas reworking. is that intended?
965 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
966 new_views.push_back (_editor->latest_regionviews.front());
970 rv->region()->clear_history ();
973 motion on the same track. plonk the previously reparented region
974 back to its original canvas group (its streamview).
975 No need to do anything for copies as they are fake regions which will be deleted.
978 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
979 rv->get_canvas_group()->property_y() = i->initial_y;
980 rv->get_time_axis_view().reveal_dependent_views (*rv);
982 /* just change the model */
984 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
986 if (dest_rtv->view()->layer_display() == Stacked) {
987 rv->region()->set_layer (dest_layer);
988 rv->region()->set_pending_explicit_relayer (true);
991 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
993 frozen_insert_result = frozen_playlists.insert(playlist);
995 if (frozen_insert_result.second) {
999 cerr << "Moving region " << rv->region()->id() << endl;
1001 rv->region()->set_position (where, (void*) this);
1003 sdc.push_back (new StatefulDiffCommand (rv->region()));
1006 if (changed_tracks && !_copy) {
1008 /* get the playlist where this drag started. we can't use rv->region()->playlist()
1009 because we may have copied the region and it has not been attached to a playlist.
1012 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
1013 tr = source_tv->track();
1014 from_playlist = tr->playlist();
1018 assert (from_playlist);
1020 /* moved to a different audio track, without copying */
1022 /* the region that used to be in the old playlist is not
1023 moved to the new one - we use a copy of it. as a result,
1024 any existing editor for the region should no longer be
1028 rv->hide_region_editor();
1029 rv->fake_set_opaque (false);
1031 /* remove the region from the old playlist */
1033 insert_result = modified_playlists.insert (from_playlist);
1035 if (insert_result.second) {
1036 from_playlist->clear_history ();
1039 cerr << "From playlist " << from_playlist->name() << " region history contains "
1040 << from_playlist->region_list().change().added.size() << " adds and "
1041 << from_playlist->region_list().change().removed.size() << " removes\n";
1043 cerr << "removing region " << rv->region() << endl;
1045 from_playlist->remove_region (rv->region());
1047 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1048 was selected in all of them, then removing it from a playlist will have removed all
1049 trace of it from the selection (i.e. there were N regions selected, we removed 1,
1050 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1051 corresponding regionview, and the selection is now empty).
1053 this could have invalidated any and all iterators into the region selection.
1055 the heuristic we use here is: if the region selection is empty, break out of the loop
1056 here. if the region selection is not empty, then restart the loop because we know that
1057 we must have removed at least the region(view) we've just been working on as well as any
1058 that we processed on previous iterations.
1060 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
1061 we can just iterate.
1064 if (_views.empty()) {
1066 sdc.push_back (new StatefulDiffCommand (to_playlist));
1067 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1070 if (from_playlist && (from_playlist != to_playlist)) {
1071 sdc.push_back (new StatefulDiffCommand (from_playlist));
1072 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1084 copies.push_back (rv);
1087 cerr << "Done with TV, top = " << to_playlist << " from = " << from_playlist << endl;
1090 sdc.push_back (new StatefulDiffCommand (to_playlist));
1091 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1094 if (from_playlist && (from_playlist != to_playlist)) {
1095 sdc.push_back (new StatefulDiffCommand (from_playlist));
1096 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1101 if we've created new regions either by copying or moving
1102 to a new track, we want to replace the old selection with the new ones
1105 if (new_views.size() > 0) {
1106 _editor->selection->set (new_views);
1109 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1114 for (vector<StatefulDiffCommand*>::iterator i = sdc.begin(); i != sdc.end(); ++i) {
1115 _editor->session()->add_command (*i);
1118 _editor->commit_reversible_command ();
1120 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1126 RegionMoveDrag::aborted ()
1130 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1137 RegionMotionDrag::aborted ();
1142 RegionMotionDrag::aborted ()
1144 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1145 RegionView* rv = i->view;
1146 TimeAxisView* tv = &(rv->get_time_axis_view ());
1147 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1149 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1150 rv->get_canvas_group()->property_y() = 0;
1151 rv->get_time_axis_view().reveal_dependent_views (*rv);
1152 rv->fake_set_opaque (false);
1153 rv->move (-_total_x_delta, 0);
1154 rv->set_height (rtv->view()->child_height ());
1157 _editor->update_canvas_now ();
1162 RegionMotionDrag::x_move_allowed () const
1164 if (Config->get_edit_mode() == Lock) {
1165 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1166 return _x_constrained;
1169 return !_x_constrained;
1173 RegionMotionDrag::copy_regions (GdkEvent* event)
1175 /* duplicate the regionview(s) and region(s) */
1177 list<DraggingView> new_regionviews;
1179 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1181 RegionView* rv = i->view;
1182 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1183 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1185 const boost::shared_ptr<const Region> original = rv->region();
1186 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1187 region_copy->set_position (original->position(), this);
1191 boost::shared_ptr<AudioRegion> audioregion_copy
1192 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1194 nrv = new AudioRegionView (*arv, audioregion_copy);
1196 boost::shared_ptr<MidiRegion> midiregion_copy
1197 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1198 nrv = new MidiRegionView (*mrv, midiregion_copy);
1203 nrv->get_canvas_group()->show ();
1204 new_regionviews.push_back (DraggingView (nrv));
1206 /* swap _primary to the copy */
1208 if (rv == _primary) {
1212 /* ..and deselect the one we copied */
1214 rv->set_selected (false);
1217 if (new_regionviews.empty()) {
1221 /* reflect the fact that we are dragging the copies */
1223 _views = new_regionviews;
1225 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1228 sync the canvas to what we think is its current state
1229 without it, the canvas seems to
1230 "forget" to update properly after the upcoming reparent()
1231 ..only if the mouse is in rapid motion at the time of the grab.
1232 something to do with regionview creation taking so long?
1234 _editor->update_canvas_now();
1238 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1240 /* Which trackview is this ? */
1242 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1243 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1244 (*layer) = tvp.second;
1246 if (*tv && (*tv)->layer_display() == Overlaid) {
1250 /* The region motion is only processed if the pointer is over
1254 if (!(*tv) || !(*tv)->is_track()) {
1255 /* To make sure we hide the verbose canvas cursor when the mouse is
1256 not held over and audiotrack.
1258 _editor->hide_verbose_canvas_cursor ();
1265 /** @param new_order New track order.
1266 * @param old_order Old track order.
1267 * @param visible_y_low Lowest visible order.
1268 * @return true if y movement should not happen, otherwise false.
1271 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1273 if (new_order != old_order) {
1275 /* this isn't the pointer track */
1279 /* moving up the canvas */
1280 if ( (new_order - y_span) >= tavs.visible_y_low) {
1284 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1285 int32_t visible_tracks = 0;
1286 while (visible_tracks < y_span ) {
1288 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1289 /* passing through a hidden track */
1294 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1295 /* moving to a non-track; disallow */
1301 /* moving beyond the lowest visible track; disallow */
1305 } else if (y_span < 0) {
1307 /* moving down the canvas */
1308 if ((new_order - y_span) <= tavs.visible_y_high) {
1310 int32_t visible_tracks = 0;
1312 while (visible_tracks > y_span ) {
1315 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1316 /* passing through a hidden track */
1321 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1322 /* moving to a non-track; disallow */
1329 /* moving beyond the highest visible track; disallow */
1336 /* this is the pointer's track */
1338 if ((new_order - y_span) > tavs.visible_y_high) {
1339 /* we will overflow */
1341 } else if ((new_order - y_span) < tavs.visible_y_low) {
1342 /* we will overflow */
1351 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1352 : RegionMotionDrag (e, i, p, v, b),
1355 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1357 _dest_trackview = tv;
1358 if (tv->layer_display() == Overlaid) {
1361 _dest_layer = _primary->region()->layer ();
1365 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1366 if (rtv && rtv->is_track()) {
1367 speed = rtv->track()->speed ();
1370 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1374 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1376 RegionMotionDrag::start_grab (event, c);
1378 _pointer_frame_offset = grab_frame() - _last_frame_position;
1381 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1382 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1384 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1385 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1387 _primary = v->view()->create_region_view (r, false, false);
1389 _primary->get_canvas_group()->show ();
1390 _primary->set_position (pos, 0);
1391 _views.push_back (DraggingView (_primary));
1393 _last_frame_position = pos;
1395 _item = _primary->get_canvas_group ();
1396 _dest_trackview = v;
1397 _dest_layer = _primary->region()->layer ();
1400 map<RegionView*, pair<RouteTimeAxisView*, int> >
1401 RegionMotionDrag::find_time_axis_views_and_layers ()
1403 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1405 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1407 double ix1, ix2, iy1, iy2;
1408 RegionView* rv = i->view;
1409 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1410 rv->get_canvas_frame()->i2w (ix1, iy1);
1411 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1413 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1414 tav[rv] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1422 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1424 _editor->update_canvas_now ();
1426 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1428 RouteTimeAxisView* dest_rtv = final[_primary].first;
1430 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1431 _primary->get_canvas_group()->property_y() = 0;
1433 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1435 _editor->begin_reversible_command (_("insert region"));
1436 playlist->clear_history ();
1437 playlist->add_region (_primary->region (), _last_frame_position);
1438 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1439 _editor->commit_reversible_command ();
1447 RegionInsertDrag::aborted ()
1454 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1455 : RegionMoveDrag (e, i, p, v, false, false)
1460 struct RegionSelectionByPosition {
1461 bool operator() (RegionView*a, RegionView* b) {
1462 return a->region()->position () < b->region()->position();
1467 RegionSpliceDrag::motion (GdkEvent* event, bool)
1469 RouteTimeAxisView* tv;
1472 if (!check_possible (&tv, &layer)) {
1478 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1484 RegionSelection copy (_editor->selection->regions);
1486 RegionSelectionByPosition cmp;
1489 nframes64_t const pf = adjusted_current_frame (event);
1491 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1493 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1499 boost::shared_ptr<Playlist> playlist;
1501 if ((playlist = atv->playlist()) == 0) {
1505 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1510 if (pf < (*i)->region()->last_frame() + 1) {
1514 if (pf > (*i)->region()->first_frame()) {
1520 playlist->shuffle ((*i)->region(), dir);
1525 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1531 RegionSpliceDrag::aborted ()
1536 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1544 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1546 _dest_trackview = _view;
1548 Drag::start_grab (event);
1553 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1556 // TODO: create region-create-drag region view here
1559 // TODO: resize region-create-drag region view here
1563 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1565 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1571 if (!movement_occurred) {
1572 mtv->add_region (grab_frame ());
1574 motion (event, false);
1575 // TODO: create region-create-drag region here
1580 RegionCreateDrag::aborted ()
1585 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1593 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1596 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1598 Drag::start_grab (event);
1600 region = &cnote->region_view();
1602 double region_start = region->get_position_pixels();
1603 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1605 if (grab_x() <= middle_point) {
1606 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1609 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1613 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1615 if (event->motion.state & Keyboard::PrimaryModifier) {
1621 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1623 if (ms.size() > 1) {
1624 /* has to be relative, may make no sense otherwise */
1628 /* select this note; if it is already selected, preserve the existing selection,
1629 otherwise make this note the only one selected.
1631 region->note_selected (cnote, cnote->selected ());
1633 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1634 MidiRegionSelection::iterator next;
1637 (*r)->begin_resizing (at_front);
1643 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1645 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1646 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1647 (*r)->update_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1652 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1654 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1655 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1656 (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1661 NoteResizeDrag::aborted ()
1667 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1673 RegionGainDrag::finished (GdkEvent *, bool)
1679 RegionGainDrag::aborted ()
1684 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1685 : RegionDrag (e, i, p, v)
1686 , _have_transaction (false)
1692 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1695 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1696 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1698 if (tv && tv->is_track()) {
1699 speed = tv->track()->speed();
1702 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1703 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1704 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1706 Drag::start_grab (event, _editor->trimmer_cursor);
1708 nframes64_t const pf = adjusted_current_frame (event);
1710 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1711 _operation = ContentsTrim;
1713 /* These will get overridden for a point trim.*/
1714 if (pf < (region_start + region_length/2)) {
1715 /* closer to start */
1716 _operation = StartTrim;
1717 } else if (pf > (region_end - region_length/2)) {
1719 _operation = EndTrim;
1723 switch (_operation) {
1725 _editor->show_verbose_time_cursor (region_start, 10);
1728 _editor->show_verbose_time_cursor (region_end, 10);
1731 _editor->show_verbose_time_cursor (pf, 10);
1737 TrimDrag::motion (GdkEvent* event, bool first_move)
1739 RegionView* rv = _primary;
1741 /* snap modifier works differently here..
1742 its current state has to be passed to the
1743 various trim functions in order to work properly
1747 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1748 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1749 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1751 if (tv && tv->is_track()) {
1752 speed = tv->track()->speed();
1755 nframes64_t const pf = adjusted_current_frame (event);
1761 switch (_operation) {
1763 trim_type = "Region start trim";
1766 trim_type = "Region end trim";
1769 trim_type = "Region content trim";
1773 _editor->begin_reversible_command (trim_type);
1774 _have_transaction = true;
1776 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1777 RegionView* rv = i->view;
1778 rv->fake_set_opaque(false);
1779 rv->region()->clear_history ();
1780 rv->region()->suspend_property_changes ();
1782 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1785 arv->temporarily_hide_envelope ();
1788 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1789 insert_result = _editor->motion_frozen_playlists.insert (pl);
1791 if (insert_result.second) {
1797 bool non_overlap_trim = false;
1799 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1800 non_overlap_trim = true;
1803 switch (_operation) {
1805 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1806 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1811 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1812 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1818 bool swap_direction = false;
1820 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1821 swap_direction = true;
1824 nframes64_t frame_delta = 0;
1826 bool left_direction = false;
1827 if (last_pointer_frame() > pf) {
1828 left_direction = true;
1831 if (left_direction) {
1832 frame_delta = (last_pointer_frame() - pf);
1834 frame_delta = (pf - last_pointer_frame());
1837 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1838 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1844 switch (_operation) {
1846 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1849 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1852 _editor->show_verbose_time_cursor (pf, 10);
1859 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1861 if (movement_occurred) {
1862 motion (event, false);
1864 if (!_editor->selection->selected (_primary)) {
1865 _editor->thaw_region_after_trim (*_primary);
1868 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1869 _editor->thaw_region_after_trim (*i->view);
1870 i->view->fake_set_opaque (true);
1871 if (_have_transaction) {
1872 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1876 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1880 _editor->motion_frozen_playlists.clear ();
1882 if (_have_transaction) {
1883 _editor->commit_reversible_command();
1887 /* no mouse movement */
1888 _editor->point_trim (event, adjusted_current_frame (event));
1893 TrimDrag::aborted ()
1895 /* Our motion method is changing model state, so use the Undo system
1896 to cancel. Perhaps not ideal, as this will leave an Undo point
1897 behind which may be slightly odd from the user's point of view.
1902 if (_have_transaction) {
1907 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1911 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1916 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1919 // create a dummy marker for visual representation of moving the copy.
1920 // The actual copying is not done before we reach the finish callback.
1922 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1923 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1924 *new MeterSection (_marker->meter()));
1926 _item = &new_marker->the_item ();
1927 _marker = new_marker;
1931 MetricSection& section (_marker->meter());
1933 if (!section.movable()) {
1939 Drag::start_grab (event, cursor);
1941 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1943 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1947 MeterMarkerDrag::motion (GdkEvent* event, bool)
1949 nframes64_t const pf = adjusted_current_frame (event);
1951 _marker->set_position (pf);
1953 _editor->show_verbose_time_cursor (pf, 10);
1957 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1959 if (!movement_occurred) {
1963 motion (event, false);
1967 TempoMap& map (_editor->session()->tempo_map());
1968 map.bbt_time (last_pointer_frame(), when);
1970 if (_copy == true) {
1971 _editor->begin_reversible_command (_("copy meter mark"));
1972 XMLNode &before = map.get_state();
1973 map.add_meter (_marker->meter(), when);
1974 XMLNode &after = map.get_state();
1975 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1976 _editor->commit_reversible_command ();
1978 // delete the dummy marker we used for visual representation of copying.
1979 // a new visual marker will show up automatically.
1982 _editor->begin_reversible_command (_("move meter mark"));
1983 XMLNode &before = map.get_state();
1984 map.move_meter (_marker->meter(), when);
1985 XMLNode &after = map.get_state();
1986 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1987 _editor->commit_reversible_command ();
1992 MeterMarkerDrag::aborted ()
1994 _marker->set_position (_marker->meter().frame ());
1997 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2001 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2006 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2011 // create a dummy marker for visual representation of moving the copy.
2012 // The actual copying is not done before we reach the finish callback.
2014 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2015 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2016 *new TempoSection (_marker->tempo()));
2018 _item = &new_marker->the_item ();
2019 _marker = new_marker;
2023 MetricSection& section (_marker->tempo());
2025 if (!section.movable()) {
2030 Drag::start_grab (event, cursor);
2032 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2033 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2037 TempoMarkerDrag::motion (GdkEvent* event, bool)
2039 nframes64_t const pf = adjusted_current_frame (event);
2040 _marker->set_position (pf);
2041 _editor->show_verbose_time_cursor (pf, 10);
2045 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2047 if (!movement_occurred) {
2051 motion (event, false);
2055 TempoMap& map (_editor->session()->tempo_map());
2056 map.bbt_time (last_pointer_frame(), when);
2058 if (_copy == true) {
2059 _editor->begin_reversible_command (_("copy tempo mark"));
2060 XMLNode &before = map.get_state();
2061 map.add_tempo (_marker->tempo(), when);
2062 XMLNode &after = map.get_state();
2063 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2064 _editor->commit_reversible_command ();
2066 // delete the dummy marker we used for visual representation of copying.
2067 // a new visual marker will show up automatically.
2070 _editor->begin_reversible_command (_("move tempo mark"));
2071 XMLNode &before = map.get_state();
2072 map.move_tempo (_marker->tempo(), when);
2073 XMLNode &after = map.get_state();
2074 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2075 _editor->commit_reversible_command ();
2080 TempoMarkerDrag::aborted ()
2082 _marker->set_position (_marker->tempo().frame());
2085 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2089 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2094 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2096 Drag::start_grab (event, c);
2100 nframes64_t where = _editor->event_frame (event, 0, 0);
2102 _editor->snap_to_with_modifier (where, event);
2103 _editor->playhead_cursor->set_position (where);
2107 if (_cursor == _editor->playhead_cursor) {
2108 _editor->_dragging_playhead = true;
2110 if (_editor->session() && _was_rolling && _stop) {
2111 _editor->session()->request_stop ();
2114 if (_editor->session() && _editor->session()->is_auditioning()) {
2115 _editor->session()->cancel_audition ();
2119 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2121 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2125 CursorDrag::motion (GdkEvent* event, bool)
2127 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2129 if (adjusted_frame == last_pointer_frame()) {
2133 _cursor->set_position (adjusted_frame);
2135 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2138 _editor->update_canvas_now ();
2140 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2144 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2146 _editor->_dragging_playhead = false;
2148 if (!movement_occurred && _stop) {
2152 motion (event, false);
2154 if (_item == &_editor->playhead_cursor->canvas_item) {
2155 if (_editor->session()) {
2156 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2157 _editor->_pending_locate_request = true;
2163 CursorDrag::aborted ()
2165 _editor->_dragging_playhead = false;
2166 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2169 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2170 : RegionDrag (e, i, p, v)
2176 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2178 Drag::start_grab (event, cursor);
2180 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2181 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2183 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2184 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2188 FadeInDrag::motion (GdkEvent* event, bool)
2190 nframes64_t fade_length;
2192 nframes64_t const pos = adjusted_current_frame (event);
2194 boost::shared_ptr<Region> region = _primary->region ();
2196 if (pos < (region->position() + 64)) {
2197 fade_length = 64; // this should be a minimum defined somewhere
2198 } else if (pos > region->last_frame()) {
2199 fade_length = region->length();
2201 fade_length = pos - region->position();
2204 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2206 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2212 tmp->reset_fade_in_shape_width (fade_length);
2215 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2219 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2221 if (!movement_occurred) {
2225 nframes64_t fade_length;
2227 nframes64_t const pos = adjusted_current_frame (event);
2229 boost::shared_ptr<Region> region = _primary->region ();
2231 if (pos < (region->position() + 64)) {
2232 fade_length = 64; // this should be a minimum defined somewhere
2233 } else if (pos > region->last_frame()) {
2234 fade_length = region->length();
2236 fade_length = pos - region->position();
2239 _editor->begin_reversible_command (_("change fade in length"));
2241 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2243 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2249 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2250 XMLNode &before = alist->get_state();
2252 tmp->audio_region()->set_fade_in_length (fade_length);
2253 tmp->audio_region()->set_fade_in_active (true);
2255 XMLNode &after = alist->get_state();
2256 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2259 _editor->commit_reversible_command ();
2263 FadeInDrag::aborted ()
2265 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2266 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2272 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2276 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2277 : RegionDrag (e, i, p, v)
2283 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2285 Drag::start_grab (event, cursor);
2287 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2288 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2290 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2291 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2295 FadeOutDrag::motion (GdkEvent* event, bool)
2297 nframes64_t fade_length;
2299 nframes64_t const pos = adjusted_current_frame (event);
2301 boost::shared_ptr<Region> region = _primary->region ();
2303 if (pos > (region->last_frame() - 64)) {
2304 fade_length = 64; // this should really be a minimum fade defined somewhere
2306 else if (pos < region->position()) {
2307 fade_length = region->length();
2310 fade_length = region->last_frame() - pos;
2313 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2315 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2321 tmp->reset_fade_out_shape_width (fade_length);
2324 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2328 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2330 if (!movement_occurred) {
2334 nframes64_t fade_length;
2336 nframes64_t const pos = adjusted_current_frame (event);
2338 boost::shared_ptr<Region> region = _primary->region ();
2340 if (pos > (region->last_frame() - 64)) {
2341 fade_length = 64; // this should really be a minimum fade defined somewhere
2343 else if (pos < region->position()) {
2344 fade_length = region->length();
2347 fade_length = region->last_frame() - pos;
2350 _editor->begin_reversible_command (_("change fade out length"));
2352 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2354 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2360 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2361 XMLNode &before = alist->get_state();
2363 tmp->audio_region()->set_fade_out_length (fade_length);
2364 tmp->audio_region()->set_fade_out_active (true);
2366 XMLNode &after = alist->get_state();
2367 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2370 _editor->commit_reversible_command ();
2374 FadeOutDrag::aborted ()
2376 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2377 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2383 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2387 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2390 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2393 _points.push_back (Gnome::Art::Point (0, 0));
2394 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2396 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2397 _line->property_width_pixels() = 1;
2398 _line->property_points () = _points;
2401 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2404 MarkerDrag::~MarkerDrag ()
2406 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2412 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2414 Drag::start_grab (event, cursor);
2418 Location *location = _editor->find_location_from_marker (_marker, is_start);
2419 _editor->_dragging_edit_point = true;
2421 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2423 update_item (location);
2425 // _drag_line->show();
2426 // _line->raise_to_top();
2429 _editor->show_verbose_time_cursor (location->start(), 10);
2431 _editor->show_verbose_time_cursor (location->end(), 10);
2434 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2437 case Selection::Toggle:
2438 _editor->selection->toggle (_marker);
2440 case Selection::Set:
2441 if (!_editor->selection->selected (_marker)) {
2442 _editor->selection->set (_marker);
2445 case Selection::Extend:
2447 Locations::LocationList ll;
2448 list<Marker*> to_add;
2450 _editor->selection->markers.range (s, e);
2451 s = min (_marker->position(), s);
2452 e = max (_marker->position(), e);
2455 if (e < max_frames) {
2458 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2459 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2460 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2463 to_add.push_back (lm->start);
2466 to_add.push_back (lm->end);
2470 if (!to_add.empty()) {
2471 _editor->selection->add (to_add);
2475 case Selection::Add:
2476 _editor->selection->add (_marker);
2480 /* Set up copies for us to manipulate during the drag */
2482 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2483 Location* l = _editor->find_location_from_marker (*i, is_start);
2484 _copied_locations.push_back (new Location (*l));
2489 MarkerDrag::motion (GdkEvent* event, bool)
2491 nframes64_t f_delta = 0;
2493 bool move_both = false;
2495 Location *real_location;
2496 Location *copy_location = 0;
2498 nframes64_t const newframe = adjusted_current_frame (event);
2500 nframes64_t next = newframe;
2502 if (newframe == last_pointer_frame()) {
2506 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2510 MarkerSelection::iterator i;
2511 list<Location*>::iterator x;
2513 /* find the marker we're dragging, and compute the delta */
2515 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2516 x != _copied_locations.end() && i != _editor->selection->markers.end();
2522 if (marker == _marker) {
2524 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2529 if (real_location->is_mark()) {
2530 f_delta = newframe - copy_location->start();
2534 switch (marker->type()) {
2536 case Marker::LoopStart:
2537 case Marker::PunchIn:
2538 f_delta = newframe - copy_location->start();
2542 case Marker::LoopEnd:
2543 case Marker::PunchOut:
2544 f_delta = newframe - copy_location->end();
2547 /* what kind of marker is this ? */
2555 if (i == _editor->selection->markers.end()) {
2556 /* hmm, impossible - we didn't find the dragged marker */
2560 /* now move them all */
2562 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2563 x != _copied_locations.end() && i != _editor->selection->markers.end();
2569 /* call this to find out if its the start or end */
2571 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2575 if (real_location->locked()) {
2579 if (copy_location->is_mark()) {
2583 copy_location->set_start (copy_location->start() + f_delta);
2587 nframes64_t new_start = copy_location->start() + f_delta;
2588 nframes64_t new_end = copy_location->end() + f_delta;
2590 if (is_start) { // start-of-range marker
2593 copy_location->set_start (new_start);
2594 copy_location->set_end (new_end);
2595 } else if (new_start < copy_location->end()) {
2596 copy_location->set_start (new_start);
2598 _editor->snap_to (next, 1, true);
2599 copy_location->set_end (next);
2600 copy_location->set_start (newframe);
2603 } else { // end marker
2606 copy_location->set_end (new_end);
2607 copy_location->set_start (new_start);
2608 } else if (new_end > copy_location->start()) {
2609 copy_location->set_end (new_end);
2610 } else if (newframe > 0) {
2611 _editor->snap_to (next, -1, true);
2612 copy_location->set_start (next);
2613 copy_location->set_end (newframe);
2618 update_item (copy_location);
2620 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2623 lm->set_position (copy_location->start(), copy_location->end());
2627 assert (!_copied_locations.empty());
2629 _editor->show_verbose_time_cursor (newframe, 10);
2632 _editor->update_canvas_now ();
2637 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2639 if (!movement_occurred) {
2641 /* just a click, do nothing but finish
2642 off the selection process
2645 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2648 case Selection::Set:
2649 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2650 _editor->selection->set (_marker);
2654 case Selection::Toggle:
2655 case Selection::Extend:
2656 case Selection::Add:
2663 _editor->_dragging_edit_point = false;
2665 _editor->begin_reversible_command ( _("move marker") );
2666 XMLNode &before = _editor->session()->locations()->get_state();
2668 MarkerSelection::iterator i;
2669 list<Location*>::iterator x;
2672 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2673 x != _copied_locations.end() && i != _editor->selection->markers.end();
2676 Location * location = _editor->find_location_from_marker (*i, is_start);
2680 if (location->locked()) {
2684 if (location->is_mark()) {
2685 location->set_start ((*x)->start());
2687 location->set ((*x)->start(), (*x)->end());
2692 XMLNode &after = _editor->session()->locations()->get_state();
2693 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2694 _editor->commit_reversible_command ();
2700 MarkerDrag::aborted ()
2706 MarkerDrag::update_item (Location* location)
2708 double const x1 = _editor->frame_to_pixel (location->start());
2710 _points.front().set_x(x1);
2711 _points.back().set_x(x1);
2712 _line->property_points() = _points;
2715 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2717 _cumulative_x_drag (0),
2718 _cumulative_y_drag (0)
2720 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2726 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2728 Drag::start_grab (event, _editor->fader_cursor);
2730 // start the grab at the center of the control point so
2731 // the point doesn't 'jump' to the mouse after the first drag
2732 _time_axis_view_grab_x = _point->get_x();
2733 _time_axis_view_grab_y = _point->get_y();
2735 float const fraction = 1 - (_point->get_y() / _point->line().height());
2737 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2739 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2740 event->button.x + 10, event->button.y + 10);
2742 _editor->show_verbose_canvas_cursor ();
2746 ControlPointDrag::motion (GdkEvent* event, bool)
2748 double dx = _drags->current_pointer_x() - last_pointer_x();
2749 double dy = _drags->current_pointer_y() - last_pointer_y();
2751 if (event->button.state & Keyboard::SecondaryModifier) {
2756 /* coordinate in TimeAxisView's space */
2757 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2758 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2760 // calculate zero crossing point. back off by .01 to stay on the
2761 // positive side of zero
2762 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2764 // make sure we hit zero when passing through
2765 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2769 if (_x_constrained) {
2770 cx = _time_axis_view_grab_x;
2772 if (_y_constrained) {
2773 cy = _time_axis_view_grab_y;
2776 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2777 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2781 cy = min ((double) _point->line().height(), cy);
2783 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2785 if (!_x_constrained) {
2786 _editor->snap_to_with_modifier (cx_frames, event);
2789 float const fraction = 1.0 - (cy / _point->line().height());
2791 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2793 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2795 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2799 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2801 if (!movement_occurred) {
2805 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2806 _editor->reset_point_selection ();
2810 motion (event, false);
2812 _point->line().end_drag ();
2816 ControlPointDrag::aborted ()
2818 _point->line().reset ();
2822 ControlPointDrag::active (Editing::MouseMode m)
2824 if (m == Editing::MouseGain) {
2825 /* always active in mouse gain */
2829 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2830 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2833 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2836 _cumulative_y_drag (0)
2841 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2843 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2846 _item = &_line->grab_item ();
2848 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2849 origin, and ditto for y.
2852 double cx = event->button.x;
2853 double cy = event->button.y;
2855 _line->parent_group().w2i (cx, cy);
2857 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2862 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2863 /* no adjacent points */
2867 Drag::start_grab (event, _editor->fader_cursor);
2869 /* store grab start in parent frame */
2871 _time_axis_view_grab_x = cx;
2872 _time_axis_view_grab_y = cy;
2874 double fraction = 1.0 - (cy / _line->height());
2876 _line->start_drag_line (before, after, fraction);
2878 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2879 event->button.x + 10, event->button.y + 10);
2881 _editor->show_verbose_canvas_cursor ();
2885 LineDrag::motion (GdkEvent* event, bool)
2887 double dy = _drags->current_pointer_y() - last_pointer_y();
2889 if (event->button.state & Keyboard::SecondaryModifier) {
2893 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2895 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2898 cy = min ((double) _line->height(), cy);
2900 double const fraction = 1.0 - (cy / _line->height());
2904 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2910 /* we are ignoring x position for this drag, so we can just pass in anything */
2911 _line->drag_motion (0, fraction, true, push);
2913 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2917 LineDrag::finished (GdkEvent* event, bool)
2919 motion (event, false);
2924 LineDrag::aborted ()
2930 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2932 Drag::start_grab (event);
2933 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2937 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2944 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2946 nframes64_t grab = grab_frame ();
2947 if (Config->get_rubberbanding_snaps_to_grid ()) {
2948 _editor->snap_to_with_modifier (grab, event);
2951 /* base start and end on initial click position */
2961 if (_drags->current_pointer_y() < grab_y()) {
2962 y1 = _drags->current_pointer_y();
2965 y2 = _drags->current_pointer_y();
2970 if (start != end || y1 != y2) {
2972 double x1 = _editor->frame_to_pixel (start);
2973 double x2 = _editor->frame_to_pixel (end);
2975 _editor->rubberband_rect->property_x1() = x1;
2976 _editor->rubberband_rect->property_y1() = y1;
2977 _editor->rubberband_rect->property_x2() = x2;
2978 _editor->rubberband_rect->property_y2() = y2;
2980 _editor->rubberband_rect->show();
2981 _editor->rubberband_rect->raise_to_top();
2983 _editor->show_verbose_time_cursor (pf, 10);
2988 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2990 if (movement_occurred) {
2992 motion (event, false);
2995 if (_drags->current_pointer_y() < grab_y()) {
2996 y1 = _drags->current_pointer_y();
2999 y2 = _drags->current_pointer_y();
3004 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3007 _editor->begin_reversible_command (_("rubberband selection"));
3009 if (grab_frame() < last_pointer_frame()) {
3010 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3012 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3016 _editor->commit_reversible_command ();
3020 if (!getenv("ARDOUR_SAE")) {
3021 _editor->selection->clear_tracks();
3023 _editor->selection->clear_regions();
3024 _editor->selection->clear_points ();
3025 _editor->selection->clear_lines ();
3028 _editor->rubberband_rect->hide();
3032 RubberbandSelectDrag::aborted ()
3034 _editor->rubberband_rect->hide ();
3038 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3040 Drag::start_grab (event);
3042 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3046 TimeFXDrag::motion (GdkEvent* event, bool)
3048 RegionView* rv = _primary;
3050 nframes64_t const pf = adjusted_current_frame (event);
3052 if (pf > rv->region()->position()) {
3053 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3056 _editor->show_verbose_time_cursor (pf, 10);
3060 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3062 _primary->get_time_axis_view().hide_timestretch ();
3064 if (!movement_occurred) {
3068 if (last_pointer_frame() < _primary->region()->position()) {
3069 /* backwards drag of the left edge - not usable */
3073 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3075 float percentage = (double) newlen / (double) _primary->region()->length();
3077 #ifndef USE_RUBBERBAND
3078 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3079 if (_primary->region()->data_type() == DataType::AUDIO) {
3080 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3084 _editor->begin_reversible_command (_("timestretch"));
3086 // XXX how do timeFX on multiple regions ?
3091 if (_editor->time_stretch (rs, percentage) == -1) {
3092 error << _("An error occurred while executing time stretch operation") << endmsg;
3097 TimeFXDrag::aborted ()
3099 _primary->get_time_axis_view().hide_timestretch ();
3104 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3106 Drag::start_grab (event);
3110 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3112 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3116 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3118 if (movement_occurred && _editor->session()) {
3119 /* make sure we stop */
3120 _editor->session()->request_transport_speed (0.0);
3125 ScrubDrag::aborted ()
3130 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3134 , _original_pointer_time_axis (-1)
3135 , _last_pointer_time_axis (-1)
3141 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3143 nframes64_t start = 0;
3144 nframes64_t end = 0;
3146 if (_editor->session() == 0) {
3150 Gdk::Cursor* cursor = 0;
3152 switch (_operation) {
3153 case CreateSelection:
3154 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3159 cursor = _editor->selector_cursor;
3160 Drag::start_grab (event, cursor);
3163 case SelectionStartTrim:
3164 if (_editor->clicked_axisview) {
3165 _editor->clicked_axisview->order_selection_trims (_item, true);
3167 Drag::start_grab (event, _editor->trimmer_cursor);
3168 start = _editor->selection->time[_editor->clicked_selection].start;
3169 _pointer_frame_offset = grab_frame() - start;
3172 case SelectionEndTrim:
3173 if (_editor->clicked_axisview) {
3174 _editor->clicked_axisview->order_selection_trims (_item, false);
3176 Drag::start_grab (event, _editor->trimmer_cursor);
3177 end = _editor->selection->time[_editor->clicked_selection].end;
3178 _pointer_frame_offset = grab_frame() - end;
3182 start = _editor->selection->time[_editor->clicked_selection].start;
3183 Drag::start_grab (event, cursor);
3184 _pointer_frame_offset = grab_frame() - start;
3188 if (_operation == SelectionMove) {
3189 _editor->show_verbose_time_cursor (start, 10);
3191 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3194 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3198 SelectionDrag::motion (GdkEvent* event, bool first_move)
3200 nframes64_t start = 0;
3201 nframes64_t end = 0;
3204 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3205 if (pending_time_axis.first == 0) {
3209 nframes64_t const pending_position = adjusted_current_frame (event);
3211 /* only alter selection if things have changed */
3213 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3217 switch (_operation) {
3218 case CreateSelection:
3220 nframes64_t grab = grab_frame ();
3223 _editor->snap_to (grab);
3226 if (pending_position < grab_frame()) {
3227 start = pending_position;
3230 end = pending_position;
3234 /* first drag: Either add to the selection
3235 or create a new selection
3241 /* adding to the selection */
3242 _editor->selection->add (_editor->clicked_axisview);
3243 _editor->clicked_selection = _editor->selection->add (start, end);
3248 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3249 _editor->selection->set (_editor->clicked_axisview);
3252 _editor->clicked_selection = _editor->selection->set (start, end);
3256 /* select the track that we're in */
3257 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3258 _editor->selection->add (pending_time_axis.first);
3259 _added_time_axes.push_back (pending_time_axis.first);
3262 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3263 tracks that we selected in the first place.
3266 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3267 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3269 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3270 while (i != _added_time_axes.end()) {
3272 list<TimeAxisView*>::iterator tmp = i;
3275 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3276 _editor->selection->remove (*i);
3277 _added_time_axes.remove (*i);
3286 case SelectionStartTrim:
3288 start = _editor->selection->time[_editor->clicked_selection].start;
3289 end = _editor->selection->time[_editor->clicked_selection].end;
3291 if (pending_position > end) {
3294 start = pending_position;
3298 case SelectionEndTrim:
3300 start = _editor->selection->time[_editor->clicked_selection].start;
3301 end = _editor->selection->time[_editor->clicked_selection].end;
3303 if (pending_position < start) {
3306 end = pending_position;
3313 start = _editor->selection->time[_editor->clicked_selection].start;
3314 end = _editor->selection->time[_editor->clicked_selection].end;
3316 length = end - start;
3318 start = pending_position;
3319 _editor->snap_to (start);
3321 end = start + length;
3326 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3327 _editor->start_canvas_autoscroll (1, 0);
3331 _editor->selection->replace (_editor->clicked_selection, start, end);
3334 if (_operation == SelectionMove) {
3335 _editor->show_verbose_time_cursor(start, 10);
3337 _editor->show_verbose_time_cursor(pending_position, 10);
3342 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3344 Session* s = _editor->session();
3346 if (movement_occurred) {
3347 motion (event, false);
3348 /* XXX this is not object-oriented programming at all. ick */
3349 if (_editor->selection->time.consolidate()) {
3350 _editor->selection->TimeChanged ();
3353 /* XXX what if its a music time selection? */
3354 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3355 s->request_play_range (&_editor->selection->time, true);
3360 /* just a click, no pointer movement.*/
3362 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3363 _editor->selection->clear_time();
3366 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3367 _editor->selection->set (_editor->clicked_axisview);
3370 if (s && s->get_play_range () && s->transport_rolling()) {
3371 s->request_stop (false, false);
3376 _editor->stop_canvas_autoscroll ();
3380 SelectionDrag::aborted ()
3385 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3390 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3391 _drag_rect->hide ();
3393 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3394 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3398 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3400 if (_editor->session() == 0) {
3404 Gdk::Cursor* cursor = 0;
3406 if (!_editor->temp_location) {
3407 _editor->temp_location = new Location;
3410 switch (_operation) {
3411 case CreateRangeMarker:
3412 case CreateTransportMarker:
3413 case CreateCDMarker:
3415 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3420 cursor = _editor->selector_cursor;
3424 Drag::start_grab (event, cursor);
3426 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3430 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3432 nframes64_t start = 0;
3433 nframes64_t end = 0;
3434 ArdourCanvas::SimpleRect *crect;
3436 switch (_operation) {
3437 case CreateRangeMarker:
3438 crect = _editor->range_bar_drag_rect;
3440 case CreateTransportMarker:
3441 crect = _editor->transport_bar_drag_rect;
3443 case CreateCDMarker:
3444 crect = _editor->cd_marker_bar_drag_rect;
3447 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3452 nframes64_t const pf = adjusted_current_frame (event);
3454 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3455 nframes64_t grab = grab_frame ();
3456 _editor->snap_to (grab);
3458 if (pf < grab_frame()) {
3466 /* first drag: Either add to the selection
3467 or create a new selection.
3472 _editor->temp_location->set (start, end);
3476 update_item (_editor->temp_location);
3478 //_drag_rect->raise_to_top();
3483 if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3484 _editor->start_canvas_autoscroll (1, 0);
3488 _editor->temp_location->set (start, end);
3490 double x1 = _editor->frame_to_pixel (start);
3491 double x2 = _editor->frame_to_pixel (end);
3492 crect->property_x1() = x1;
3493 crect->property_x2() = x2;
3495 update_item (_editor->temp_location);
3498 _editor->show_verbose_time_cursor (pf, 10);
3503 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3505 Location * newloc = 0;
3509 if (movement_occurred) {
3510 motion (event, false);
3513 switch (_operation) {
3514 case CreateRangeMarker:
3515 case CreateCDMarker:
3517 _editor->begin_reversible_command (_("new range marker"));
3518 XMLNode &before = _editor->session()->locations()->get_state();
3519 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3520 if (_operation == CreateCDMarker) {
3521 flags = Location::IsRangeMarker | Location::IsCDMarker;
3522 _editor->cd_marker_bar_drag_rect->hide();
3525 flags = Location::IsRangeMarker;
3526 _editor->range_bar_drag_rect->hide();
3528 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3529 _editor->session()->locations()->add (newloc, true);
3530 XMLNode &after = _editor->session()->locations()->get_state();
3531 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3532 _editor->commit_reversible_command ();
3536 case CreateTransportMarker:
3537 // popup menu to pick loop or punch
3538 _editor->new_transport_marker_context_menu (&event->button, _item);
3542 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3544 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3549 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3551 if (end == max_frames) {
3552 end = _editor->session()->current_end_frame ();
3555 if (start == max_frames) {
3556 start = _editor->session()->current_start_frame ();
3559 switch (_editor->mouse_mode) {
3561 /* find the two markers on either side and then make the selection from it */
3562 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3566 /* find the two markers on either side of the click and make the range out of it */
3567 _editor->selection->set (start, end);
3576 _editor->stop_canvas_autoscroll ();
3580 RangeMarkerBarDrag::aborted ()
3586 RangeMarkerBarDrag::update_item (Location* location)
3588 double const x1 = _editor->frame_to_pixel (location->start());
3589 double const x2 = _editor->frame_to_pixel (location->end());
3591 _drag_rect->property_x1() = x1;
3592 _drag_rect->property_x2() = x2;
3596 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3598 Drag::start_grab (event, _editor->zoom_cursor);
3599 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3603 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3608 nframes64_t const pf = adjusted_current_frame (event);
3610 nframes64_t grab = grab_frame ();
3611 _editor->snap_to_with_modifier (grab, event);
3613 /* base start and end on initial click position */
3625 _editor->zoom_rect->show();
3626 _editor->zoom_rect->raise_to_top();
3629 _editor->reposition_zoom_rect(start, end);
3631 _editor->show_verbose_time_cursor (pf, 10);
3636 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3638 if (movement_occurred) {
3639 motion (event, false);
3641 if (grab_frame() < last_pointer_frame()) {
3642 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3644 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3647 _editor->temporal_zoom_to_frame (false, grab_frame());
3649 temporal_zoom_step (false);
3650 center_screen (grab_frame());
3654 _editor->zoom_rect->hide();
3658 MouseZoomDrag::aborted ()
3660 _editor->zoom_rect->hide ();
3663 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3666 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3667 region = &cnote->region_view();
3671 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3673 Drag::start_grab (event);
3676 drag_delta_note = 0;
3681 event_x = _drags->current_pointer_x();
3682 event_y = _drags->current_pointer_y();
3684 _item->property_parent().get_value()->w2i(event_x, event_y);
3686 last_x = region->snap_to_pixel(event_x);
3689 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3691 if (!(was_selected = cnote->selected())) {
3693 /* tertiary-click means extend selection - we'll do that on button release,
3694 so don't add it here, because otherwise we make it hard to figure
3695 out the "extend-to" range.
3698 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3701 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3704 region->note_selected (cnote, true);
3706 region->unique_select (cnote);
3713 NoteDrag::motion (GdkEvent*, bool)
3715 MidiStreamView* streamview = region->midi_stream_view();
3719 event_x = _drags->current_pointer_x();
3720 event_y = _drags->current_pointer_y();
3722 _item->property_parent().get_value()->w2i(event_x, event_y);
3724 event_x = region->snap_to_pixel(event_x);
3726 double dx = event_x - last_x;
3727 double dy = event_y - last_y;
3732 // Snap to note rows
3734 if (abs (dy) < streamview->note_height()) {
3737 int8_t this_delta_note;
3739 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3741 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3743 drag_delta_note -= this_delta_note;
3744 dy = streamview->note_height() * this_delta_note;
3745 last_y = last_y + dy;
3749 region->move_selection (dx, dy);
3751 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3754 snprintf (buf, sizeof (buf), "%s (%g)", Evoral::midi_note_name (cnote->note()->note()).c_str(),
3755 (int) cnote->note()->note() + drag_delta_note);
3756 _editor->show_verbose_canvas_cursor_with (buf);
3761 NoteDrag::finished (GdkEvent* ev, bool moved)
3763 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3766 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3769 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3771 region->note_deselected (cnote);
3774 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3775 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3777 if (!extend && !add && region->selection_size() > 1) {
3778 region->unique_select(cnote);
3779 } else if (extend) {
3780 region->note_selected (cnote, true, true);
3782 /* it was added during button press */
3787 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3792 NoteDrag::aborted ()
3797 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3800 , _nothing_to_drag (false)
3802 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3805 _line = _atav->line ();
3809 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3811 Drag::start_grab (event, cursor);
3813 list<ControlPoint*> points;
3815 XMLNode* state = &_line->get_state ();
3817 if (_ranges.empty()) {
3819 uint32_t const N = _line->npoints ();
3820 for (uint32_t i = 0; i < N; ++i) {
3821 points.push_back (_line->nth (i));
3826 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3827 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3829 /* fade into and out of the region that we're dragging;
3830 64 samples length plucked out of thin air.
3832 nframes64_t const h = (j->start + j->end) / 2;
3833 nframes64_t a = j->start + 64;
3837 nframes64_t b = j->end - 64;
3842 the_list->add (j->start, the_list->eval (j->start));
3843 _line->add_always_in_view (j->start);
3844 the_list->add (a, the_list->eval (a));
3845 _line->add_always_in_view (a);
3846 the_list->add (b, the_list->eval (b));
3847 _line->add_always_in_view (b);
3848 the_list->add (j->end, the_list->eval (j->end));
3849 _line->add_always_in_view (j->end);
3852 uint32_t const N = _line->npoints ();
3853 for (uint32_t i = 0; i < N; ++i) {
3855 ControlPoint* p = _line->nth (i);
3857 list<AudioRange>::const_iterator j = _ranges.begin ();
3858 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3862 if (j != _ranges.end()) {
3863 points.push_back (p);
3868 if (points.empty()) {
3869 _nothing_to_drag = true;
3873 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3877 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3879 if (_nothing_to_drag) {
3883 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3885 /* we are ignoring x position for this drag, so we can just pass in anything */
3886 _line->drag_motion (0, f, true, false);
3890 AutomationRangeDrag::finished (GdkEvent* event, bool)
3892 if (_nothing_to_drag) {
3896 motion (event, false);
3898 _line->clear_always_in_view ();
3902 AutomationRangeDrag::aborted ()
3904 _line->clear_always_in_view ();
3908 DraggingView::DraggingView (RegionView* v)
3911 initial_y = v->get_canvas_group()->property_y ();