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 ()
73 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
82 DragManager::break_drag ()
86 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
97 DragManager::add (Drag* d)
99 d->set_manager (this);
100 _drags.push_back (d);
104 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
106 assert (_drags.empty ());
107 d->set_manager (this);
108 _drags.push_back (d);
113 DragManager::start_grab (GdkEvent* e)
115 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
117 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
118 (*i)->start_grab (e);
123 DragManager::end_grab (GdkEvent* e)
128 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
129 bool const t = (*i)->end_grab (e);
144 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
148 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
150 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
151 bool const t = (*i)->motion_handler (e, from_autoscroll);
162 DragManager::have_item (ArdourCanvas::Item* i) const
164 list<Drag*>::const_iterator j = _drags.begin ();
165 while (j != _drags.end() && (*j)->item () != i) {
169 return j != _drags.end ();
172 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
175 , _pointer_frame_offset (0)
176 , _move_threshold_passed (false)
178 , _last_pointer_frame (0)
184 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
190 cursor = _editor->which_grabber_cursor ();
193 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
197 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
200 cursor = _editor->which_grabber_cursor ();
203 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
205 if (Keyboard::is_button2_event (&event->button)) {
206 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
207 _y_constrained = true;
208 _x_constrained = false;
210 _y_constrained = false;
211 _x_constrained = true;
214 _x_constrained = false;
215 _y_constrained = false;
218 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
219 _grab_frame = adjusted_frame (_grab_frame, event);
220 _last_pointer_frame = _grab_frame;
221 _last_pointer_x = _grab_x;
222 _last_pointer_y = _grab_y;
224 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
228 if (_editor->session() && _editor->session()->transport_rolling()) {
231 _was_rolling = false;
234 switch (_editor->snap_type()) {
235 case SnapToRegionStart:
236 case SnapToRegionEnd:
237 case SnapToRegionSync:
238 case SnapToRegionBoundary:
239 _editor->build_region_boundary_cache ();
246 /** @param event GDK event, or 0.
247 * @return true if some movement occurred, otherwise false.
250 Drag::end_grab (GdkEvent* event)
252 _editor->stop_canvas_autoscroll ();
254 _item->ungrab (event ? event->button.time : 0);
256 finished (event, _move_threshold_passed);
258 _editor->hide_verbose_canvas_cursor();
260 return _move_threshold_passed;
264 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
268 if (f > _pointer_frame_offset) {
269 pos = f - _pointer_frame_offset;
273 _editor->snap_to_with_modifier (pos, event);
280 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
282 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
286 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
288 /* check to see if we have moved in any way that matters since the last motion event */
289 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
290 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
294 pair<nframes64_t, int> const threshold = move_threshold ();
296 bool const old_move_threshold_passed = _move_threshold_passed;
298 if (!from_autoscroll && !_move_threshold_passed) {
300 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
301 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
303 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
306 if (active (_editor->mouse_mode) && _move_threshold_passed) {
308 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
309 if (!from_autoscroll) {
310 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
313 motion (event, _move_threshold_passed != old_move_threshold_passed);
315 _last_pointer_x = _drags->current_pointer_x ();
316 _last_pointer_y = _drags->current_pointer_y ();
317 _last_pointer_frame = adjusted_current_frame (event);
335 _editor->stop_canvas_autoscroll ();
336 _editor->hide_verbose_canvas_cursor ();
339 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
343 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
344 _views.push_back (DraggingView (*i));
347 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
351 RegionDrag::region_going_away (RegionView* v)
353 list<DraggingView>::iterator i = _views.begin ();
354 while (i != _views.end() && i->view != v) {
358 if (i != _views.end()) {
363 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
364 : RegionDrag (e, i, p, v),
375 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
377 Drag::start_grab (event);
379 _editor->show_verbose_time_cursor (_last_frame_position, 10);
382 RegionMotionDrag::TimeAxisViewSummary
383 RegionMotionDrag::get_time_axis_view_summary ()
385 int32_t children = 0;
386 TimeAxisViewSummary sum;
388 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
390 /* get a bitmask representing the visible tracks */
392 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
393 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
394 TimeAxisView::Children children_list;
396 /* zeroes are audio/MIDI tracks. ones are other types. */
398 if (!rtv->hidden()) {
400 if (!rtv->is_track()) {
401 /* not an audio nor MIDI track */
402 sum.tracks = sum.tracks |= (0x01 << rtv->order());
405 sum.height_list[rtv->order()] = (*i)->current_height();
408 if ((children_list = rtv->get_child_list()).size() > 0) {
409 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
410 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
411 sum.height_list[rtv->order() + children] = (*j)->current_height();
422 RegionMotionDrag::compute_y_delta (
423 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
424 int32_t last_pointer_layer, int32_t current_pointer_layer,
425 TimeAxisViewSummary const & tavs,
426 int32_t* pointer_order_span, int32_t* pointer_layer_span,
427 int32_t* canvas_pointer_order_span
431 *pointer_order_span = 0;
432 *pointer_layer_span = 0;
436 bool clamp_y_axis = false;
438 /* the change in track order between this callback and the last */
439 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
440 /* the change in layer between this callback and the last;
441 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
442 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
444 if (*pointer_order_span != 0) {
446 /* find the actual pointer span, in terms of the number of visible tracks;
447 to do this, we reduce |pointer_order_span| by the number of hidden tracks
450 *canvas_pointer_order_span = *pointer_order_span;
451 if (last_pointer_view->order() >= current_pointer_view->order()) {
452 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
453 if (tavs.height_list[y] == 0) {
454 *canvas_pointer_order_span--;
458 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
459 if (tavs.height_list[y] == 0) {
460 *canvas_pointer_order_span++;
465 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
467 RegionView* rv = i->view;
469 if (rv->region()->locked()) {
473 double ix1, ix2, iy1, iy2;
474 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
475 rv->get_canvas_frame()->i2w (ix1, iy1);
476 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
478 /* get the new trackview for this particular region */
479 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
481 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
483 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
484 as surely this is a per-region thing... */
486 clamp_y_axis = y_movement_disallowed (
487 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
495 } else if (_dest_trackview == current_pointer_view) {
497 if (current_pointer_layer == last_pointer_layer) {
498 /* No movement; clamp */
504 _dest_trackview = current_pointer_view;
505 _dest_layer = current_pointer_layer;
513 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
515 /* compute the amount of pointer motion in frames, and where
516 the region would be if we moved it by that much.
518 *pending_region_position = adjusted_current_frame (event);
520 nframes64_t sync_frame;
521 nframes64_t sync_offset;
524 sync_offset = _primary->region()->sync_offset (sync_dir);
526 /* we don't handle a sync point that lies before zero.
528 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
530 sync_frame = *pending_region_position + (sync_dir*sync_offset);
532 _editor->snap_to_with_modifier (sync_frame, event);
534 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
537 *pending_region_position = _last_frame_position;
540 if (*pending_region_position > max_frames - _primary->region()->length()) {
541 *pending_region_position = _last_frame_position;
546 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
548 /* now compute the canvas unit distance we need to move the regionview
549 to make it appear at the new location.
552 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
554 if (*pending_region_position <= _last_frame_position) {
556 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
558 RegionView* rv = i->view;
560 // If any regionview is at zero, we need to know so we can stop further leftward motion.
562 double ix1, ix2, iy1, iy2;
563 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
564 rv->get_canvas_frame()->i2w (ix1, iy1);
566 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
568 *pending_region_position = _last_frame_position;
575 _last_frame_position = *pending_region_position;
582 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
586 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
588 vector<int32_t>::iterator j;
590 /* *pointer* variables reflect things about the pointer; as we may be moving
591 multiple regions, much detail must be computed per-region */
593 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
594 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
595 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
596 is always 0 regardless of what the region's "real" layer is */
597 RouteTimeAxisView* current_pointer_view;
598 layer_t current_pointer_layer;
599 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
603 /* TimeAxisView that we were pointing at last time we entered this method */
604 TimeAxisView const * const last_pointer_view = _dest_trackview;
605 /* the order of the track that we were pointing at last time we entered this method */
606 int32_t const last_pointer_order = last_pointer_view->order ();
607 /* the layer that we were pointing at last time we entered this method */
608 layer_t const last_pointer_layer = _dest_layer;
610 int32_t pointer_order_span;
611 int32_t pointer_layer_span;
612 int32_t canvas_pointer_order_span;
614 bool const clamp_y_axis = compute_y_delta (
615 last_pointer_view, current_pointer_view,
616 last_pointer_layer, current_pointer_layer, tavs,
617 &pointer_order_span, &pointer_layer_span,
618 &canvas_pointer_order_span
621 nframes64_t pending_region_position;
622 double const x_delta = compute_x_delta (event, &pending_region_position);
624 /*************************************************************
626 ************************************************************/
628 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
629 /* haven't reached next snap point, and we're not switching
630 trackviews nor layers. nothing to do.
635 /*************************************************************
637 ************************************************************/
639 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
641 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
643 RegionView* rv = i->view;
645 if (rv->region()->locked()) {
649 /* here we are calculating the y distance from the
650 top of the first track view to the top of the region
651 area of the track view that we're working on */
653 /* this x value is just a dummy value so that we have something
658 /* distance from the top of this track view to the region area
659 of our track view is always 1 */
663 /* convert to world coordinates, ie distance from the top of
666 rv->get_canvas_frame()->i2w (ix1, iy1);
668 /* compensate for the ruler section and the vertical scrollbar position */
669 iy1 += _editor->get_trackview_group_vertical_offset ();
673 // hide any dependent views
675 rv->get_time_axis_view().hide_dependent_views (*rv);
678 reparent to a non scrolling group so that we can keep the
679 region selection above all time axis views.
680 reparenting means we have to move the rv as the two
681 parent groups have different coordinates.
684 rv->get_canvas_group()->property_y() = iy1 - 1;
685 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
687 rv->fake_set_opaque (true);
690 /* current view for this particular region */
691 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
692 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
694 if (pointer_order_span != 0 && !clamp_y_axis) {
696 /* INTER-TRACK MOVEMENT */
698 /* move through the height list to the track that the region is currently on */
699 vector<int32_t>::iterator j = tavs.height_list.begin ();
701 while (j != tavs.height_list.end () && x != rtv->order ()) {
707 int32_t temp_pointer_order_span = canvas_pointer_order_span;
709 if (j != tavs.height_list.end ()) {
711 /* Account for layers in the original and
712 destination tracks. If we're moving around in layers we assume
713 that only one track is involved, so it's ok to use *pointer*
716 StreamView* lv = last_pointer_view->view ();
719 /* move to the top of the last trackview */
720 if (lv->layer_display () == Stacked) {
721 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
724 StreamView* cv = current_pointer_view->view ();
727 /* move to the right layer on the current trackview */
728 if (cv->layer_display () == Stacked) {
729 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
732 /* And for being on a non-topmost layer on the new
735 while (temp_pointer_order_span > 0) {
736 /* we're moving up canvas-wise,
737 so we need to find the next track height
739 if (j != tavs.height_list.begin()) {
743 if (x != last_pointer_order) {
745 ++temp_pointer_order_span;
750 temp_pointer_order_span--;
753 while (temp_pointer_order_span < 0) {
757 if (x != last_pointer_order) {
759 --temp_pointer_order_span;
763 if (j != tavs.height_list.end()) {
767 temp_pointer_order_span++;
771 /* find out where we'll be when we move and set height accordingly */
773 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
774 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
775 rv->set_height (temp_rtv->view()->child_height());
777 /* if you un-comment the following, the region colours will follow
778 the track colours whilst dragging; personally
779 i think this can confuse things, but never mind.
782 //const GdkColor& col (temp_rtv->view->get_region_color());
783 //rv->set_color (const_cast<GdkColor&>(col));
787 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
789 /* INTER-LAYER MOVEMENT in the same track */
790 y_delta = rtv->view()->child_height () * pointer_layer_span;
795 _editor->mouse_brush_insert_region (rv, pending_region_position);
797 rv->move (x_delta, y_delta);
800 } /* foreach region */
802 _total_x_delta += x_delta;
805 _editor->cursor_group->raise_to_top();
808 if (x_delta != 0 && !_brushing) {
809 _editor->show_verbose_time_cursor (_last_frame_position, 10);
814 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
816 if (_copy && first_move) {
817 copy_regions (event);
820 RegionMotionDrag::motion (event, first_move);
824 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
826 vector<RegionView*> copies;
827 boost::shared_ptr<Track> tr;
828 boost::shared_ptr<Playlist> from_playlist;
829 boost::shared_ptr<Playlist> to_playlist;
830 RegionSelection new_views;
831 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
832 PlaylistSet modified_playlists;
833 PlaylistSet frozen_playlists;
834 list <sigc::connection> modified_playlist_connections;
835 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
836 nframes64_t drag_delta;
837 bool changed_tracks, changed_position;
838 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
839 RouteTimeAxisView* source_tv;
840 vector<StatefulDiffCommand*> sdc;
842 if (!movement_occurred) {
848 /* all changes were made during motion event handlers */
851 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
852 copies.push_back (i->view);
859 /* reverse this here so that we have the correct logic to finalize
863 if (Config->get_edit_mode() == Lock) {
864 _x_constrained = !_x_constrained;
868 if (_x_constrained) {
869 _editor->begin_reversible_command (_("fixed time region copy"));
871 _editor->begin_reversible_command (_("region copy"));
874 if (_x_constrained) {
875 _editor->begin_reversible_command (_("fixed time region drag"));
877 _editor->begin_reversible_command (_("region drag"));
881 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
882 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
884 drag_delta = _primary->region()->position() - _last_frame_position;
886 _editor->update_canvas_now ();
888 /* make a list of where each region ended up */
889 final = find_time_axis_views_and_layers ();
891 cerr << "Iterate over " << _views.size() << " views\n";
893 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
895 RegionView* rv = i->view;
896 RouteTimeAxisView* dest_rtv = final[rv].first;
897 layer_t dest_layer = final[rv].second;
901 from_playlist.reset ();
902 to_playlist.reset ();
904 if (rv->region()->locked()) {
909 if (changed_position && !_x_constrained) {
910 where = rv->region()->position() - drag_delta;
912 where = rv->region()->position();
915 boost::shared_ptr<Region> new_region;
918 /* we already made a copy */
919 new_region = rv->region();
921 /* undo the previous hide_dependent_views so that xfades don't
922 disappear on copying regions
925 //rv->get_time_axis_view().reveal_dependent_views (*rv);
927 } else if (changed_tracks && dest_rtv->playlist()) {
928 new_region = RegionFactory::create (rv->region());
931 if (changed_tracks || _copy) {
933 to_playlist = dest_rtv->playlist();
940 _editor->latest_regionviews.clear ();
942 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
944 insert_result = modified_playlists.insert (to_playlist);
946 if (insert_result.second) {
947 to_playlist->clear_history ();
950 cerr << "To playlist " << to_playlist->name() << " region history contains "
951 << to_playlist->region_list().change().added.size() << " adds and "
952 << to_playlist->region_list().change().removed.size() << " removes\n";
954 cerr << "Adding new region " << new_region->id() << " based on "
955 << rv->region()->id() << endl;
957 to_playlist->add_region (new_region, where);
959 if (dest_rtv->view()->layer_display() == Stacked) {
960 new_region->set_layer (dest_layer);
961 new_region->set_pending_explicit_relayer (true);
966 if (!_editor->latest_regionviews.empty()) {
967 // XXX why just the first one ? we only expect one
968 // commented out in nick_m's canvas reworking. is that intended?
969 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
970 new_views.push_back (_editor->latest_regionviews.front());
974 rv->region()->clear_history ();
977 motion on the same track. plonk the previously reparented region
978 back to its original canvas group (its streamview).
979 No need to do anything for copies as they are fake regions which will be deleted.
982 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
983 rv->get_canvas_group()->property_y() = i->initial_y;
984 rv->get_time_axis_view().reveal_dependent_views (*rv);
986 /* just change the model */
988 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
990 if (dest_rtv->view()->layer_display() == Stacked) {
991 rv->region()->set_layer (dest_layer);
992 rv->region()->set_pending_explicit_relayer (true);
995 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
997 frozen_insert_result = frozen_playlists.insert(playlist);
999 if (frozen_insert_result.second) {
1003 cerr << "Moving region " << rv->region()->id() << endl;
1005 rv->region()->set_position (where, (void*) this);
1007 sdc.push_back (new StatefulDiffCommand (rv->region()));
1010 if (changed_tracks && !_copy) {
1012 /* get the playlist where this drag started. we can't use rv->region()->playlist()
1013 because we may have copied the region and it has not been attached to a playlist.
1016 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
1017 tr = source_tv->track();
1018 from_playlist = tr->playlist();
1022 assert (from_playlist);
1024 /* moved to a different audio track, without copying */
1026 /* the region that used to be in the old playlist is not
1027 moved to the new one - we use a copy of it. as a result,
1028 any existing editor for the region should no longer be
1032 rv->hide_region_editor();
1033 rv->fake_set_opaque (false);
1035 /* remove the region from the old playlist */
1037 insert_result = modified_playlists.insert (from_playlist);
1039 if (insert_result.second) {
1040 from_playlist->clear_history ();
1043 cerr << "From playlist " << from_playlist->name() << " region history contains "
1044 << from_playlist->region_list().change().added.size() << " adds and "
1045 << from_playlist->region_list().change().removed.size() << " removes\n";
1047 cerr << "removing region " << rv->region() << endl;
1049 from_playlist->remove_region (rv->region());
1051 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1052 was selected in all of them, then removing it from a playlist will have removed all
1053 trace of it from the selection (i.e. there were N regions selected, we removed 1,
1054 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1055 corresponding regionview, and the selection is now empty).
1057 this could have invalidated any and all iterators into the region selection.
1059 the heuristic we use here is: if the region selection is empty, break out of the loop
1060 here. if the region selection is not empty, then restart the loop because we know that
1061 we must have removed at least the region(view) we've just been working on as well as any
1062 that we processed on previous iterations.
1064 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
1065 we can just iterate.
1068 if (_views.empty()) {
1070 sdc.push_back (new StatefulDiffCommand (to_playlist));
1071 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1074 if (from_playlist && (from_playlist != to_playlist)) {
1075 sdc.push_back (new StatefulDiffCommand (from_playlist));
1076 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1088 copies.push_back (rv);
1091 cerr << "Done with TV, top = " << to_playlist << " from = " << from_playlist << endl;
1094 sdc.push_back (new StatefulDiffCommand (to_playlist));
1095 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1098 if (from_playlist && (from_playlist != to_playlist)) {
1099 sdc.push_back (new StatefulDiffCommand (from_playlist));
1100 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1105 if we've created new regions either by copying or moving
1106 to a new track, we want to replace the old selection with the new ones
1109 if (new_views.size() > 0) {
1110 _editor->selection->set (new_views);
1113 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1118 for (vector<StatefulDiffCommand*>::iterator i = sdc.begin(); i != sdc.end(); ++i) {
1119 _editor->session()->add_command (*i);
1122 _editor->commit_reversible_command ();
1124 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1130 RegionMoveDrag::aborted ()
1134 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1141 RegionMotionDrag::aborted ();
1146 RegionMotionDrag::aborted ()
1148 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1149 RegionView* rv = i->view;
1150 TimeAxisView* tv = &(rv->get_time_axis_view ());
1151 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1153 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1154 rv->get_canvas_group()->property_y() = 0;
1155 rv->get_time_axis_view().reveal_dependent_views (*rv);
1156 rv->fake_set_opaque (false);
1157 rv->move (-_total_x_delta, 0);
1158 rv->set_height (rtv->view()->child_height ());
1161 _editor->update_canvas_now ();
1166 RegionMotionDrag::x_move_allowed () const
1168 if (Config->get_edit_mode() == Lock) {
1169 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1170 return _x_constrained;
1173 return !_x_constrained;
1177 RegionMotionDrag::copy_regions (GdkEvent* event)
1179 /* duplicate the regionview(s) and region(s) */
1181 list<DraggingView> new_regionviews;
1183 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1185 RegionView* rv = i->view;
1186 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1187 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1189 const boost::shared_ptr<const Region> original = rv->region();
1190 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1191 region_copy->set_position (original->position(), this);
1195 boost::shared_ptr<AudioRegion> audioregion_copy
1196 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1198 nrv = new AudioRegionView (*arv, audioregion_copy);
1200 boost::shared_ptr<MidiRegion> midiregion_copy
1201 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1202 nrv = new MidiRegionView (*mrv, midiregion_copy);
1207 nrv->get_canvas_group()->show ();
1208 new_regionviews.push_back (DraggingView (nrv));
1210 /* swap _primary to the copy */
1212 if (rv == _primary) {
1216 /* ..and deselect the one we copied */
1218 rv->set_selected (false);
1221 if (new_regionviews.empty()) {
1225 /* reflect the fact that we are dragging the copies */
1227 _views = new_regionviews;
1229 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1232 sync the canvas to what we think is its current state
1233 without it, the canvas seems to
1234 "forget" to update properly after the upcoming reparent()
1235 ..only if the mouse is in rapid motion at the time of the grab.
1236 something to do with regionview creation taking so long?
1238 _editor->update_canvas_now();
1242 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1244 /* Which trackview is this ? */
1246 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1247 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1248 (*layer) = tvp.second;
1250 if (*tv && (*tv)->layer_display() == Overlaid) {
1254 /* The region motion is only processed if the pointer is over
1258 if (!(*tv) || !(*tv)->is_track()) {
1259 /* To make sure we hide the verbose canvas cursor when the mouse is
1260 not held over and audiotrack.
1262 _editor->hide_verbose_canvas_cursor ();
1269 /** @param new_order New track order.
1270 * @param old_order Old track order.
1271 * @param visible_y_low Lowest visible order.
1272 * @return true if y movement should not happen, otherwise false.
1275 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1277 if (new_order != old_order) {
1279 /* this isn't the pointer track */
1283 /* moving up the canvas */
1284 if ( (new_order - y_span) >= tavs.visible_y_low) {
1288 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1289 int32_t visible_tracks = 0;
1290 while (visible_tracks < y_span ) {
1292 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1293 /* passing through a hidden track */
1298 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1299 /* moving to a non-track; disallow */
1305 /* moving beyond the lowest visible track; disallow */
1309 } else if (y_span < 0) {
1311 /* moving down the canvas */
1312 if ((new_order - y_span) <= tavs.visible_y_high) {
1314 int32_t visible_tracks = 0;
1316 while (visible_tracks > y_span ) {
1319 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1320 /* passing through a hidden track */
1325 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1326 /* moving to a non-track; disallow */
1333 /* moving beyond the highest visible track; disallow */
1340 /* this is the pointer's track */
1342 if ((new_order - y_span) > tavs.visible_y_high) {
1343 /* we will overflow */
1345 } else if ((new_order - y_span) < tavs.visible_y_low) {
1346 /* we will overflow */
1355 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1356 : RegionMotionDrag (e, i, p, v, b),
1359 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1361 _dest_trackview = tv;
1362 if (tv->layer_display() == Overlaid) {
1365 _dest_layer = _primary->region()->layer ();
1369 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1370 if (rtv && rtv->is_track()) {
1371 speed = rtv->track()->speed ();
1374 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1378 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1380 RegionMotionDrag::start_grab (event, c);
1382 _pointer_frame_offset = grab_frame() - _last_frame_position;
1385 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1386 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1388 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1389 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1391 _primary = v->view()->create_region_view (r, false, false);
1393 _primary->get_canvas_group()->show ();
1394 _primary->set_position (pos, 0);
1395 _views.push_back (DraggingView (_primary));
1397 _last_frame_position = pos;
1399 _item = _primary->get_canvas_group ();
1400 _dest_trackview = v;
1401 _dest_layer = _primary->region()->layer ();
1404 map<RegionView*, pair<RouteTimeAxisView*, int> >
1405 RegionMotionDrag::find_time_axis_views_and_layers ()
1407 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1409 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1411 double ix1, ix2, iy1, iy2;
1412 RegionView* rv = i->view;
1413 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1414 rv->get_canvas_frame()->i2w (ix1, iy1);
1415 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1417 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1418 tav[rv] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1426 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1428 _editor->update_canvas_now ();
1430 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1432 RouteTimeAxisView* dest_rtv = final[_primary].first;
1434 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1435 _primary->get_canvas_group()->property_y() = 0;
1437 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1439 _editor->begin_reversible_command (_("insert region"));
1440 playlist->clear_history ();
1441 playlist->add_region (_primary->region (), _last_frame_position);
1442 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1443 _editor->commit_reversible_command ();
1451 RegionInsertDrag::aborted ()
1456 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1457 : RegionMoveDrag (e, i, p, v, false, false)
1462 struct RegionSelectionByPosition {
1463 bool operator() (RegionView*a, RegionView* b) {
1464 return a->region()->position () < b->region()->position();
1469 RegionSpliceDrag::motion (GdkEvent* event, bool)
1471 RouteTimeAxisView* tv;
1474 if (!check_possible (&tv, &layer)) {
1480 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1486 RegionSelection copy (_editor->selection->regions);
1488 RegionSelectionByPosition cmp;
1491 nframes64_t const pf = adjusted_current_frame (event);
1493 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1495 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1501 boost::shared_ptr<Playlist> playlist;
1503 if ((playlist = atv->playlist()) == 0) {
1507 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1512 if (pf < (*i)->region()->last_frame() + 1) {
1516 if (pf > (*i)->region()->first_frame()) {
1522 playlist->shuffle ((*i)->region(), dir);
1527 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1533 RegionSpliceDrag::aborted ()
1538 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1546 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1548 _dest_trackview = _view;
1550 Drag::start_grab (event);
1555 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1558 // TODO: create region-create-drag region view here
1561 // TODO: resize region-create-drag region view here
1565 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1567 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1573 if (!movement_occurred) {
1574 mtv->add_region (grab_frame ());
1576 motion (event, false);
1577 // TODO: create region-create-drag region here
1582 RegionCreateDrag::aborted ()
1587 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1595 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1598 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1600 Drag::start_grab (event);
1602 region = &cnote->region_view();
1604 double region_start = region->get_position_pixels();
1605 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1607 if (grab_x() <= middle_point) {
1608 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1611 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1615 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1617 if (event->motion.state & Keyboard::PrimaryModifier) {
1623 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1625 if (ms.size() > 1) {
1626 /* has to be relative, may make no sense otherwise */
1630 region->note_selected (cnote, true);
1632 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1633 MidiRegionSelection::iterator next;
1636 (*r)->begin_resizing (at_front);
1642 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1644 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1645 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1646 (*r)->update_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1651 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1653 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1654 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1655 (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1660 NoteResizeDrag::aborted ()
1666 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1672 RegionGainDrag::finished (GdkEvent *, bool)
1678 RegionGainDrag::aborted ()
1683 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1684 : RegionDrag (e, i, p, v)
1685 , _have_transaction (false)
1691 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1694 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1695 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1697 if (tv && tv->is_track()) {
1698 speed = tv->track()->speed();
1701 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1702 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1703 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1705 Drag::start_grab (event, _editor->trimmer_cursor);
1707 nframes64_t const pf = adjusted_current_frame (event);
1709 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1710 _operation = ContentsTrim;
1712 /* These will get overridden for a point trim.*/
1713 if (pf < (region_start + region_length/2)) {
1714 /* closer to start */
1715 _operation = StartTrim;
1716 } else if (pf > (region_end - region_length/2)) {
1718 _operation = EndTrim;
1722 switch (_operation) {
1724 _editor->show_verbose_time_cursor (region_start, 10);
1727 _editor->show_verbose_time_cursor (region_end, 10);
1730 _editor->show_verbose_time_cursor (pf, 10);
1736 TrimDrag::motion (GdkEvent* event, bool first_move)
1738 RegionView* rv = _primary;
1739 nframes64_t frame_delta = 0;
1741 bool left_direction;
1742 bool obey_snap = event ? !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) : false;
1744 /* snap modifier works differently here..
1745 its current state has to be passed to the
1746 various trim functions in order to work properly
1750 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1751 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1752 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1754 if (tv && tv->is_track()) {
1755 speed = tv->track()->speed();
1758 nframes64_t const pf = adjusted_current_frame (event);
1760 if (last_pointer_frame() > pf) {
1761 left_direction = true;
1763 left_direction = false;
1770 switch (_operation) {
1772 trim_type = "Region start trim";
1775 trim_type = "Region end trim";
1778 trim_type = "Region content trim";
1782 _editor->begin_reversible_command (trim_type);
1783 _have_transaction = true;
1785 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1786 RegionView* rv = i->view;
1787 rv->fake_set_opaque(false);
1788 rv->region()->clear_history ();
1789 rv->region()->suspend_property_changes ();
1791 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1794 arv->temporarily_hide_envelope ();
1797 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1798 insert_result = _editor->motion_frozen_playlists.insert (pl);
1800 if (insert_result.second) {
1806 if (left_direction) {
1807 frame_delta = (last_pointer_frame() - pf);
1809 frame_delta = (pf - last_pointer_frame());
1812 bool non_overlap_trim = false;
1814 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1815 non_overlap_trim = true;
1818 switch (_operation) {
1820 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1824 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1825 _editor->single_start_trim (*i->view, frame_delta, left_direction, obey_snap, non_overlap_trim);
1831 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1835 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1836 _editor->single_end_trim (*i->view, frame_delta, left_direction, obey_snap, non_overlap_trim);
1843 bool swap_direction = false;
1845 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1846 swap_direction = true;
1849 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1850 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction, obey_snap);
1856 switch (_operation) {
1858 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1861 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1864 _editor->show_verbose_time_cursor (pf, 10);
1871 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1873 if (movement_occurred) {
1874 motion (event, false);
1876 if (!_editor->selection->selected (_primary)) {
1877 _editor->thaw_region_after_trim (*_primary);
1880 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1881 _editor->thaw_region_after_trim (*i->view);
1882 i->view->fake_set_opaque (true);
1883 if (_have_transaction) {
1884 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1888 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1892 _editor->motion_frozen_playlists.clear ();
1894 if (_have_transaction) {
1895 _editor->commit_reversible_command();
1899 /* no mouse movement */
1900 _editor->point_trim (event, adjusted_current_frame (event));
1905 TrimDrag::aborted ()
1907 /* Our motion method is changing model state, so use the Undo system
1908 to cancel. Perhaps not ideal, as this will leave an Undo point
1909 behind which may be slightly odd from the user's point of view.
1914 if (_have_transaction) {
1919 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1923 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1928 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1931 // create a dummy marker for visual representation of moving the copy.
1932 // The actual copying is not done before we reach the finish callback.
1934 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1935 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1936 *new MeterSection (_marker->meter()));
1938 _item = &new_marker->the_item ();
1939 _marker = new_marker;
1943 MetricSection& section (_marker->meter());
1945 if (!section.movable()) {
1951 Drag::start_grab (event, cursor);
1953 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1955 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1959 MeterMarkerDrag::motion (GdkEvent* event, bool)
1961 nframes64_t const pf = adjusted_current_frame (event);
1963 _marker->set_position (pf);
1965 _editor->show_verbose_time_cursor (pf, 10);
1969 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1971 if (!movement_occurred) {
1975 motion (event, false);
1979 TempoMap& map (_editor->session()->tempo_map());
1980 map.bbt_time (last_pointer_frame(), when);
1982 if (_copy == true) {
1983 _editor->begin_reversible_command (_("copy meter mark"));
1984 XMLNode &before = map.get_state();
1985 map.add_meter (_marker->meter(), when);
1986 XMLNode &after = map.get_state();
1987 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1988 _editor->commit_reversible_command ();
1990 // delete the dummy marker we used for visual representation of copying.
1991 // a new visual marker will show up automatically.
1994 _editor->begin_reversible_command (_("move meter mark"));
1995 XMLNode &before = map.get_state();
1996 map.move_meter (_marker->meter(), when);
1997 XMLNode &after = map.get_state();
1998 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1999 _editor->commit_reversible_command ();
2004 MeterMarkerDrag::aborted ()
2006 _marker->set_position (_marker->meter().frame ());
2009 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2013 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2018 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2023 // create a dummy marker for visual representation of moving the copy.
2024 // The actual copying is not done before we reach the finish callback.
2026 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2027 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2028 *new TempoSection (_marker->tempo()));
2030 _item = &new_marker->the_item ();
2031 _marker = new_marker;
2035 MetricSection& section (_marker->tempo());
2037 if (!section.movable()) {
2042 Drag::start_grab (event, cursor);
2044 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2045 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2049 TempoMarkerDrag::motion (GdkEvent* event, bool)
2051 nframes64_t const pf = adjusted_current_frame (event);
2052 _marker->set_position (pf);
2053 _editor->show_verbose_time_cursor (pf, 10);
2057 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2059 if (!movement_occurred) {
2063 motion (event, false);
2067 TempoMap& map (_editor->session()->tempo_map());
2068 map.bbt_time (last_pointer_frame(), when);
2070 if (_copy == true) {
2071 _editor->begin_reversible_command (_("copy tempo mark"));
2072 XMLNode &before = map.get_state();
2073 map.add_tempo (_marker->tempo(), when);
2074 XMLNode &after = map.get_state();
2075 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2076 _editor->commit_reversible_command ();
2078 // delete the dummy marker we used for visual representation of copying.
2079 // a new visual marker will show up automatically.
2082 _editor->begin_reversible_command (_("move tempo mark"));
2083 XMLNode &before = map.get_state();
2084 map.move_tempo (_marker->tempo(), when);
2085 XMLNode &after = map.get_state();
2086 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2087 _editor->commit_reversible_command ();
2092 TempoMarkerDrag::aborted ()
2094 _marker->set_position (_marker->tempo().frame());
2097 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2101 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2106 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2108 Drag::start_grab (event, c);
2112 nframes64_t where = _editor->event_frame (event, 0, 0);
2114 _editor->snap_to_with_modifier (where, event);
2115 _editor->playhead_cursor->set_position (where);
2119 if (_cursor == _editor->playhead_cursor) {
2120 _editor->_dragging_playhead = true;
2122 if (_editor->session() && _was_rolling && _stop) {
2123 _editor->session()->request_stop ();
2126 if (_editor->session() && _editor->session()->is_auditioning()) {
2127 _editor->session()->cancel_audition ();
2131 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2133 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2137 CursorDrag::motion (GdkEvent* event, bool)
2139 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2141 if (adjusted_frame == last_pointer_frame()) {
2145 _cursor->set_position (adjusted_frame);
2147 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2150 _editor->update_canvas_now ();
2152 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2156 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2158 _editor->_dragging_playhead = false;
2160 if (!movement_occurred && _stop) {
2164 motion (event, false);
2166 if (_item == &_editor->playhead_cursor->canvas_item) {
2167 if (_editor->session()) {
2168 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2169 _editor->_pending_locate_request = true;
2175 CursorDrag::aborted ()
2177 _editor->_dragging_playhead = false;
2178 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2181 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2182 : RegionDrag (e, i, p, v)
2188 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2190 Drag::start_grab (event, cursor);
2192 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2193 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2195 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2196 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2200 FadeInDrag::motion (GdkEvent* event, bool)
2202 nframes64_t fade_length;
2204 nframes64_t const pos = adjusted_current_frame (event);
2206 boost::shared_ptr<Region> region = _primary->region ();
2208 if (pos < (region->position() + 64)) {
2209 fade_length = 64; // this should be a minimum defined somewhere
2210 } else if (pos > region->last_frame()) {
2211 fade_length = region->length();
2213 fade_length = pos - region->position();
2216 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2218 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2224 tmp->reset_fade_in_shape_width (fade_length);
2227 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2231 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2233 if (!movement_occurred) {
2237 nframes64_t fade_length;
2239 nframes64_t const pos = adjusted_current_frame (event);
2241 boost::shared_ptr<Region> region = _primary->region ();
2243 if (pos < (region->position() + 64)) {
2244 fade_length = 64; // this should be a minimum defined somewhere
2245 } else if (pos > region->last_frame()) {
2246 fade_length = region->length();
2248 fade_length = pos - region->position();
2251 _editor->begin_reversible_command (_("change fade in length"));
2253 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2255 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2261 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2262 XMLNode &before = alist->get_state();
2264 tmp->audio_region()->set_fade_in_length (fade_length);
2265 tmp->audio_region()->set_fade_in_active (true);
2267 XMLNode &after = alist->get_state();
2268 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2271 _editor->commit_reversible_command ();
2275 FadeInDrag::aborted ()
2277 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2278 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2284 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2288 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2289 : RegionDrag (e, i, p, v)
2295 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2297 Drag::start_grab (event, cursor);
2299 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2300 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2302 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2303 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2307 FadeOutDrag::motion (GdkEvent* event, bool)
2309 nframes64_t fade_length;
2311 nframes64_t const pos = adjusted_current_frame (event);
2313 boost::shared_ptr<Region> region = _primary->region ();
2315 if (pos > (region->last_frame() - 64)) {
2316 fade_length = 64; // this should really be a minimum fade defined somewhere
2318 else if (pos < region->position()) {
2319 fade_length = region->length();
2322 fade_length = region->last_frame() - pos;
2325 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2327 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2333 tmp->reset_fade_out_shape_width (fade_length);
2336 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2340 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2342 if (!movement_occurred) {
2346 nframes64_t fade_length;
2348 nframes64_t const pos = adjusted_current_frame (event);
2350 boost::shared_ptr<Region> region = _primary->region ();
2352 if (pos > (region->last_frame() - 64)) {
2353 fade_length = 64; // this should really be a minimum fade defined somewhere
2355 else if (pos < region->position()) {
2356 fade_length = region->length();
2359 fade_length = region->last_frame() - pos;
2362 _editor->begin_reversible_command (_("change fade out length"));
2364 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2366 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2372 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2373 XMLNode &before = alist->get_state();
2375 tmp->audio_region()->set_fade_out_length (fade_length);
2376 tmp->audio_region()->set_fade_out_active (true);
2378 XMLNode &after = alist->get_state();
2379 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2382 _editor->commit_reversible_command ();
2386 FadeOutDrag::aborted ()
2388 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2389 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2395 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2399 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2402 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2405 _points.push_back (Gnome::Art::Point (0, 0));
2406 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2408 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2409 _line->property_width_pixels() = 1;
2410 _line->property_points () = _points;
2413 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2416 MarkerDrag::~MarkerDrag ()
2418 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2424 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2426 Drag::start_grab (event, cursor);
2430 Location *location = _editor->find_location_from_marker (_marker, is_start);
2431 _editor->_dragging_edit_point = true;
2433 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2435 update_item (location);
2437 // _drag_line->show();
2438 // _line->raise_to_top();
2441 _editor->show_verbose_time_cursor (location->start(), 10);
2443 _editor->show_verbose_time_cursor (location->end(), 10);
2446 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2449 case Selection::Toggle:
2450 _editor->selection->toggle (_marker);
2452 case Selection::Set:
2453 if (!_editor->selection->selected (_marker)) {
2454 _editor->selection->set (_marker);
2457 case Selection::Extend:
2459 Locations::LocationList ll;
2460 list<Marker*> to_add;
2462 _editor->selection->markers.range (s, e);
2463 s = min (_marker->position(), s);
2464 e = max (_marker->position(), e);
2467 if (e < max_frames) {
2470 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2471 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2472 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2475 to_add.push_back (lm->start);
2478 to_add.push_back (lm->end);
2482 if (!to_add.empty()) {
2483 _editor->selection->add (to_add);
2487 case Selection::Add:
2488 _editor->selection->add (_marker);
2492 /* Set up copies for us to manipulate during the drag */
2494 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2495 Location* l = _editor->find_location_from_marker (*i, is_start);
2496 _copied_locations.push_back (new Location (*l));
2501 MarkerDrag::motion (GdkEvent* event, bool)
2503 nframes64_t f_delta = 0;
2505 bool move_both = false;
2507 Location *real_location;
2508 Location *copy_location = 0;
2510 nframes64_t const newframe = adjusted_current_frame (event);
2512 nframes64_t next = newframe;
2514 if (newframe == last_pointer_frame()) {
2518 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2522 MarkerSelection::iterator i;
2523 list<Location*>::iterator x;
2525 /* find the marker we're dragging, and compute the delta */
2527 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2528 x != _copied_locations.end() && i != _editor->selection->markers.end();
2534 if (marker == _marker) {
2536 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2541 if (real_location->is_mark()) {
2542 f_delta = newframe - copy_location->start();
2546 switch (marker->type()) {
2548 case Marker::LoopStart:
2549 case Marker::PunchIn:
2550 f_delta = newframe - copy_location->start();
2554 case Marker::LoopEnd:
2555 case Marker::PunchOut:
2556 f_delta = newframe - copy_location->end();
2559 /* what kind of marker is this ? */
2567 if (i == _editor->selection->markers.end()) {
2568 /* hmm, impossible - we didn't find the dragged marker */
2572 /* now move them all */
2574 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2575 x != _copied_locations.end() && i != _editor->selection->markers.end();
2581 /* call this to find out if its the start or end */
2583 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2587 if (real_location->locked()) {
2591 if (copy_location->is_mark()) {
2595 copy_location->set_start (copy_location->start() + f_delta);
2599 nframes64_t new_start = copy_location->start() + f_delta;
2600 nframes64_t new_end = copy_location->end() + f_delta;
2602 if (is_start) { // start-of-range marker
2605 copy_location->set_start (new_start);
2606 copy_location->set_end (new_end);
2607 } else if (new_start < copy_location->end()) {
2608 copy_location->set_start (new_start);
2610 _editor->snap_to (next, 1, true);
2611 copy_location->set_end (next);
2612 copy_location->set_start (newframe);
2615 } else { // end marker
2618 copy_location->set_end (new_end);
2619 copy_location->set_start (new_start);
2620 } else if (new_end > copy_location->start()) {
2621 copy_location->set_end (new_end);
2622 } else if (newframe > 0) {
2623 _editor->snap_to (next, -1, true);
2624 copy_location->set_start (next);
2625 copy_location->set_end (newframe);
2630 update_item (copy_location);
2632 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2635 lm->set_position (copy_location->start(), copy_location->end());
2639 assert (!_copied_locations.empty());
2641 _editor->show_verbose_time_cursor (newframe, 10);
2644 _editor->update_canvas_now ();
2649 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2651 if (!movement_occurred) {
2653 /* just a click, do nothing but finish
2654 off the selection process
2657 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2660 case Selection::Set:
2661 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2662 _editor->selection->set (_marker);
2666 case Selection::Toggle:
2667 case Selection::Extend:
2668 case Selection::Add:
2675 _editor->_dragging_edit_point = false;
2677 _editor->begin_reversible_command ( _("move marker") );
2678 XMLNode &before = _editor->session()->locations()->get_state();
2680 MarkerSelection::iterator i;
2681 list<Location*>::iterator x;
2684 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2685 x != _copied_locations.end() && i != _editor->selection->markers.end();
2688 Location * location = _editor->find_location_from_marker (*i, is_start);
2692 if (location->locked()) {
2696 if (location->is_mark()) {
2697 location->set_start ((*x)->start());
2699 location->set ((*x)->start(), (*x)->end());
2704 XMLNode &after = _editor->session()->locations()->get_state();
2705 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2706 _editor->commit_reversible_command ();
2712 MarkerDrag::aborted ()
2718 MarkerDrag::update_item (Location* location)
2720 double const x1 = _editor->frame_to_pixel (location->start());
2722 _points.front().set_x(x1);
2723 _points.back().set_x(x1);
2724 _line->property_points() = _points;
2727 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2729 _cumulative_x_drag (0),
2730 _cumulative_y_drag (0)
2732 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2738 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2740 Drag::start_grab (event, _editor->fader_cursor);
2742 // start the grab at the center of the control point so
2743 // the point doesn't 'jump' to the mouse after the first drag
2744 _time_axis_view_grab_x = _point->get_x();
2745 _time_axis_view_grab_y = _point->get_y();
2747 float const fraction = 1 - (_point->get_y() / _point->line().height());
2749 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2751 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2752 event->button.x + 10, event->button.y + 10);
2754 _editor->show_verbose_canvas_cursor ();
2758 ControlPointDrag::motion (GdkEvent* event, bool)
2760 double dx = _drags->current_pointer_x() - last_pointer_x();
2761 double dy = _drags->current_pointer_y() - last_pointer_y();
2763 if (event->button.state & Keyboard::SecondaryModifier) {
2768 /* coordinate in TimeAxisView's space */
2769 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2770 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2772 // calculate zero crossing point. back off by .01 to stay on the
2773 // positive side of zero
2774 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2776 // make sure we hit zero when passing through
2777 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2781 if (_x_constrained) {
2782 cx = _time_axis_view_grab_x;
2784 if (_y_constrained) {
2785 cy = _time_axis_view_grab_y;
2788 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2789 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2793 cy = min ((double) _point->line().height(), cy);
2795 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2797 if (!_x_constrained) {
2798 _editor->snap_to_with_modifier (cx_frames, event);
2801 float const fraction = 1.0 - (cy / _point->line().height());
2803 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2805 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2807 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2811 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2813 if (!movement_occurred) {
2817 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2818 _editor->reset_point_selection ();
2822 motion (event, false);
2824 _point->line().end_drag ();
2828 ControlPointDrag::aborted ()
2830 _point->line().reset ();
2834 ControlPointDrag::active (Editing::MouseMode m)
2836 if (m == Editing::MouseGain) {
2837 /* always active in mouse gain */
2841 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2842 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2845 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2848 _cumulative_y_drag (0)
2853 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2855 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2858 _item = &_line->grab_item ();
2860 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2861 origin, and ditto for y.
2864 double cx = event->button.x;
2865 double cy = event->button.y;
2867 _line->parent_group().w2i (cx, cy);
2869 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2874 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2875 /* no adjacent points */
2879 Drag::start_grab (event, _editor->fader_cursor);
2881 /* store grab start in parent frame */
2883 _time_axis_view_grab_x = cx;
2884 _time_axis_view_grab_y = cy;
2886 double fraction = 1.0 - (cy / _line->height());
2888 _line->start_drag_line (before, after, fraction);
2890 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2891 event->button.x + 10, event->button.y + 10);
2893 _editor->show_verbose_canvas_cursor ();
2897 LineDrag::motion (GdkEvent* event, bool)
2899 double dy = _drags->current_pointer_y() - last_pointer_y();
2901 if (event->button.state & Keyboard::SecondaryModifier) {
2905 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2907 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2910 cy = min ((double) _line->height(), cy);
2912 double const fraction = 1.0 - (cy / _line->height());
2916 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2922 /* we are ignoring x position for this drag, so we can just pass in anything */
2923 _line->drag_motion (0, fraction, true, push);
2925 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2929 LineDrag::finished (GdkEvent* event, bool)
2931 motion (event, false);
2936 LineDrag::aborted ()
2942 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2944 Drag::start_grab (event);
2945 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2949 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2956 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2958 nframes64_t grab = grab_frame ();
2959 if (Config->get_rubberbanding_snaps_to_grid ()) {
2960 _editor->snap_to_with_modifier (grab, event);
2963 /* base start and end on initial click position */
2973 if (_drags->current_pointer_y() < grab_y()) {
2974 y1 = _drags->current_pointer_y();
2977 y2 = _drags->current_pointer_y();
2982 if (start != end || y1 != y2) {
2984 double x1 = _editor->frame_to_pixel (start);
2985 double x2 = _editor->frame_to_pixel (end);
2987 _editor->rubberband_rect->property_x1() = x1;
2988 _editor->rubberband_rect->property_y1() = y1;
2989 _editor->rubberband_rect->property_x2() = x2;
2990 _editor->rubberband_rect->property_y2() = y2;
2992 _editor->rubberband_rect->show();
2993 _editor->rubberband_rect->raise_to_top();
2995 _editor->show_verbose_time_cursor (pf, 10);
3000 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3002 if (movement_occurred) {
3004 motion (event, false);
3007 if (_drags->current_pointer_y() < grab_y()) {
3008 y1 = _drags->current_pointer_y();
3011 y2 = _drags->current_pointer_y();
3016 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3019 _editor->begin_reversible_command (_("rubberband selection"));
3021 if (grab_frame() < last_pointer_frame()) {
3022 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3024 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3028 _editor->commit_reversible_command ();
3032 if (!getenv("ARDOUR_SAE")) {
3033 _editor->selection->clear_tracks();
3035 _editor->selection->clear_regions();
3036 _editor->selection->clear_points ();
3037 _editor->selection->clear_lines ();
3040 _editor->rubberband_rect->hide();
3044 RubberbandSelectDrag::aborted ()
3046 _editor->rubberband_rect->hide ();
3050 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3052 Drag::start_grab (event);
3054 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3058 TimeFXDrag::motion (GdkEvent* event, bool)
3060 RegionView* rv = _primary;
3062 nframes64_t const pf = adjusted_current_frame (event);
3064 if (pf > rv->region()->position()) {
3065 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3068 _editor->show_verbose_time_cursor (pf, 10);
3072 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3074 _primary->get_time_axis_view().hide_timestretch ();
3076 if (!movement_occurred) {
3080 if (last_pointer_frame() < _primary->region()->position()) {
3081 /* backwards drag of the left edge - not usable */
3085 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3087 float percentage = (double) newlen / (double) _primary->region()->length();
3089 #ifndef USE_RUBBERBAND
3090 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3091 if (_primary->region()->data_type() == DataType::AUDIO) {
3092 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3096 _editor->begin_reversible_command (_("timestretch"));
3098 // XXX how do timeFX on multiple regions ?
3103 if (!_editor->time_stretch (rs, percentage) == 0) {
3104 error << _("An error occurred while executing time stretch operation") << endmsg;
3109 TimeFXDrag::aborted ()
3111 _primary->get_time_axis_view().hide_timestretch ();
3116 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3118 Drag::start_grab (event);
3122 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3124 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3128 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3130 if (movement_occurred && _editor->session()) {
3131 /* make sure we stop */
3132 _editor->session()->request_transport_speed (0.0);
3137 ScrubDrag::aborted ()
3142 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3146 , _original_pointer_time_axis (-1)
3147 , _last_pointer_time_axis (-1)
3153 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3155 nframes64_t start = 0;
3156 nframes64_t end = 0;
3158 if (_editor->session() == 0) {
3162 Gdk::Cursor* cursor = 0;
3164 switch (_operation) {
3165 case CreateSelection:
3166 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3171 cursor = _editor->selector_cursor;
3172 Drag::start_grab (event, cursor);
3175 case SelectionStartTrim:
3176 if (_editor->clicked_axisview) {
3177 _editor->clicked_axisview->order_selection_trims (_item, true);
3179 Drag::start_grab (event, _editor->trimmer_cursor);
3180 start = _editor->selection->time[_editor->clicked_selection].start;
3181 _pointer_frame_offset = grab_frame() - start;
3184 case SelectionEndTrim:
3185 if (_editor->clicked_axisview) {
3186 _editor->clicked_axisview->order_selection_trims (_item, false);
3188 Drag::start_grab (event, _editor->trimmer_cursor);
3189 end = _editor->selection->time[_editor->clicked_selection].end;
3190 _pointer_frame_offset = grab_frame() - end;
3194 start = _editor->selection->time[_editor->clicked_selection].start;
3195 Drag::start_grab (event, cursor);
3196 _pointer_frame_offset = grab_frame() - start;
3200 if (_operation == SelectionMove) {
3201 _editor->show_verbose_time_cursor (start, 10);
3203 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3206 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3210 SelectionDrag::motion (GdkEvent* event, bool first_move)
3212 nframes64_t start = 0;
3213 nframes64_t end = 0;
3216 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3217 if (pending_time_axis.first == 0) {
3221 nframes64_t const pending_position = adjusted_current_frame (event);
3223 /* only alter selection if things have changed */
3225 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3229 switch (_operation) {
3230 case CreateSelection:
3232 nframes64_t grab = grab_frame ();
3235 _editor->snap_to (grab);
3238 if (pending_position < grab_frame()) {
3239 start = pending_position;
3242 end = pending_position;
3246 /* first drag: Either add to the selection
3247 or create a new selection
3253 /* adding to the selection */
3254 _editor->selection->add (_editor->clicked_axisview);
3255 _editor->clicked_selection = _editor->selection->add (start, end);
3260 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3261 _editor->selection->set (_editor->clicked_axisview);
3264 _editor->clicked_selection = _editor->selection->set (start, end);
3268 /* select the track that we're in */
3269 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3270 _editor->selection->add (pending_time_axis.first);
3271 _added_time_axes.push_back (pending_time_axis.first);
3274 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3275 tracks that we selected in the first place.
3278 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3279 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3281 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3282 while (i != _added_time_axes.end()) {
3284 list<TimeAxisView*>::iterator tmp = i;
3287 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3288 _editor->selection->remove (*i);
3289 _added_time_axes.remove (*i);
3298 case SelectionStartTrim:
3300 start = _editor->selection->time[_editor->clicked_selection].start;
3301 end = _editor->selection->time[_editor->clicked_selection].end;
3303 if (pending_position > end) {
3306 start = pending_position;
3310 case SelectionEndTrim:
3312 start = _editor->selection->time[_editor->clicked_selection].start;
3313 end = _editor->selection->time[_editor->clicked_selection].end;
3315 if (pending_position < start) {
3318 end = pending_position;
3325 start = _editor->selection->time[_editor->clicked_selection].start;
3326 end = _editor->selection->time[_editor->clicked_selection].end;
3328 length = end - start;
3330 start = pending_position;
3331 _editor->snap_to (start);
3333 end = start + length;
3338 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3339 _editor->start_canvas_autoscroll (1, 0);
3343 _editor->selection->replace (_editor->clicked_selection, start, end);
3346 if (_operation == SelectionMove) {
3347 _editor->show_verbose_time_cursor(start, 10);
3349 _editor->show_verbose_time_cursor(pending_position, 10);
3354 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3356 Session* s = _editor->session();
3358 if (movement_occurred) {
3359 motion (event, false);
3360 /* XXX this is not object-oriented programming at all. ick */
3361 if (_editor->selection->time.consolidate()) {
3362 _editor->selection->TimeChanged ();
3365 /* XXX what if its a music time selection? */
3366 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3367 s->request_play_range (&_editor->selection->time, true);
3372 /* just a click, no pointer movement.*/
3374 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3375 _editor->selection->clear_time();
3378 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3379 _editor->selection->set (_editor->clicked_axisview);
3382 if (s && s->get_play_range () && s->transport_rolling()) {
3383 s->request_stop (false, false);
3388 _editor->stop_canvas_autoscroll ();
3392 SelectionDrag::aborted ()
3397 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3402 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3403 _drag_rect->hide ();
3405 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3406 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3410 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3412 if (_editor->session() == 0) {
3416 Gdk::Cursor* cursor = 0;
3418 if (!_editor->temp_location) {
3419 _editor->temp_location = new Location;
3422 switch (_operation) {
3423 case CreateRangeMarker:
3424 case CreateTransportMarker:
3425 case CreateCDMarker:
3427 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3432 cursor = _editor->selector_cursor;
3436 Drag::start_grab (event, cursor);
3438 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3442 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3444 nframes64_t start = 0;
3445 nframes64_t end = 0;
3446 ArdourCanvas::SimpleRect *crect;
3448 switch (_operation) {
3449 case CreateRangeMarker:
3450 crect = _editor->range_bar_drag_rect;
3452 case CreateTransportMarker:
3453 crect = _editor->transport_bar_drag_rect;
3455 case CreateCDMarker:
3456 crect = _editor->cd_marker_bar_drag_rect;
3459 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3464 nframes64_t const pf = adjusted_current_frame (event);
3466 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3467 nframes64_t grab = grab_frame ();
3468 _editor->snap_to (grab);
3470 if (pf < grab_frame()) {
3478 /* first drag: Either add to the selection
3479 or create a new selection.
3484 _editor->temp_location->set (start, end);
3488 update_item (_editor->temp_location);
3490 //_drag_rect->raise_to_top();
3495 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3496 _editor->start_canvas_autoscroll (1, 0);
3500 _editor->temp_location->set (start, end);
3502 double x1 = _editor->frame_to_pixel (start);
3503 double x2 = _editor->frame_to_pixel (end);
3504 crect->property_x1() = x1;
3505 crect->property_x2() = x2;
3507 update_item (_editor->temp_location);
3510 _editor->show_verbose_time_cursor (pf, 10);
3515 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3517 Location * newloc = 0;
3521 if (movement_occurred) {
3522 motion (event, false);
3525 switch (_operation) {
3526 case CreateRangeMarker:
3527 case CreateCDMarker:
3529 _editor->begin_reversible_command (_("new range marker"));
3530 XMLNode &before = _editor->session()->locations()->get_state();
3531 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3532 if (_operation == CreateCDMarker) {
3533 flags = Location::IsRangeMarker | Location::IsCDMarker;
3534 _editor->cd_marker_bar_drag_rect->hide();
3537 flags = Location::IsRangeMarker;
3538 _editor->range_bar_drag_rect->hide();
3540 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3541 _editor->session()->locations()->add (newloc, true);
3542 XMLNode &after = _editor->session()->locations()->get_state();
3543 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3544 _editor->commit_reversible_command ();
3548 case CreateTransportMarker:
3549 // popup menu to pick loop or punch
3550 _editor->new_transport_marker_context_menu (&event->button, _item);
3554 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3556 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3561 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3563 if (end == max_frames) {
3564 end = _editor->session()->current_end_frame ();
3567 if (start == max_frames) {
3568 start = _editor->session()->current_start_frame ();
3571 switch (_editor->mouse_mode) {
3573 /* find the two markers on either side and then make the selection from it */
3574 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3578 /* find the two markers on either side of the click and make the range out of it */
3579 _editor->selection->set (start, end);
3588 _editor->stop_canvas_autoscroll ();
3592 RangeMarkerBarDrag::aborted ()
3598 RangeMarkerBarDrag::update_item (Location* location)
3600 double const x1 = _editor->frame_to_pixel (location->start());
3601 double const x2 = _editor->frame_to_pixel (location->end());
3603 _drag_rect->property_x1() = x1;
3604 _drag_rect->property_x2() = x2;
3608 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3610 Drag::start_grab (event, _editor->zoom_cursor);
3611 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3615 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3620 nframes64_t const pf = adjusted_current_frame (event);
3622 nframes64_t grab = grab_frame ();
3623 _editor->snap_to_with_modifier (grab, event);
3625 /* base start and end on initial click position */
3637 _editor->zoom_rect->show();
3638 _editor->zoom_rect->raise_to_top();
3641 _editor->reposition_zoom_rect(start, end);
3643 _editor->show_verbose_time_cursor (pf, 10);
3648 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3650 if (movement_occurred) {
3651 motion (event, false);
3653 if (grab_frame() < last_pointer_frame()) {
3654 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3656 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3659 _editor->temporal_zoom_to_frame (false, grab_frame());
3661 temporal_zoom_step (false);
3662 center_screen (grab_frame());
3666 _editor->zoom_rect->hide();
3670 MouseZoomDrag::aborted ()
3672 _editor->zoom_rect->hide ();
3675 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3678 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3679 region = &cnote->region_view();
3683 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3685 Drag::start_grab (event);
3688 drag_delta_note = 0;
3693 event_x = _drags->current_pointer_x();
3694 event_y = _drags->current_pointer_y();
3696 _item->property_parent().get_value()->w2i(event_x, event_y);
3698 last_x = region->snap_to_pixel(event_x);
3701 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3703 if (!(was_selected = cnote->selected())) {
3705 /* tertiary-click means extend selection - we'll do that on button release,
3706 so don't add it here, because otherwise we make it hard to figure
3707 out the "extend-to" range.
3710 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3713 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3716 region->note_selected (cnote, true);
3718 region->unique_select (cnote);
3725 NoteDrag::motion (GdkEvent*, bool)
3727 MidiStreamView* streamview = region->midi_stream_view();
3731 event_x = _drags->current_pointer_x();
3732 event_y = _drags->current_pointer_y();
3734 _item->property_parent().get_value()->w2i(event_x, event_y);
3736 event_x = region->snap_to_pixel(event_x);
3738 double dx = event_x - last_x;
3739 double dy = event_y - last_y;
3744 // Snap to note rows
3746 if (abs (dy) < streamview->note_height()) {
3749 int8_t this_delta_note;
3751 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3753 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3755 drag_delta_note -= this_delta_note;
3756 dy = streamview->note_height() * this_delta_note;
3757 last_y = last_y + dy;
3761 region->move_selection (dx, dy);
3763 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3765 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3766 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3767 _editor->show_verbose_canvas_cursor_with (buf);
3772 NoteDrag::finished (GdkEvent* ev, bool moved)
3774 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3777 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3780 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3782 region->note_deselected (cnote);
3785 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3786 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3788 if (!extend && !add && region->selection_size() > 1) {
3789 region->unique_select(cnote);
3790 } else if (extend) {
3791 region->note_selected (cnote, true, true);
3793 /* it was added during button press */
3798 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3803 NoteDrag::aborted ()
3808 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3811 , _nothing_to_drag (false)
3813 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3816 _line = _atav->line ();
3820 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3822 Drag::start_grab (event, cursor);
3824 list<ControlPoint*> points;
3826 XMLNode* state = &_line->get_state ();
3828 if (_ranges.empty()) {
3830 uint32_t const N = _line->npoints ();
3831 for (uint32_t i = 0; i < N; ++i) {
3832 points.push_back (_line->nth (i));
3837 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3838 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3840 /* fade into and out of the region that we're dragging;
3841 64 samples length plucked out of thin air.
3843 nframes64_t const h = (j->start + j->end) / 2;
3844 nframes64_t a = j->start + 64;
3848 nframes64_t b = j->end - 64;
3853 the_list->add (j->start, the_list->eval (j->start));
3854 _line->add_always_in_view (j->start);
3855 the_list->add (a, the_list->eval (a));
3856 _line->add_always_in_view (a);
3857 the_list->add (b, the_list->eval (b));
3858 _line->add_always_in_view (b);
3859 the_list->add (j->end, the_list->eval (j->end));
3860 _line->add_always_in_view (j->end);
3863 uint32_t const N = _line->npoints ();
3864 for (uint32_t i = 0; i < N; ++i) {
3866 ControlPoint* p = _line->nth (i);
3868 list<AudioRange>::const_iterator j = _ranges.begin ();
3869 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3873 if (j != _ranges.end()) {
3874 points.push_back (p);
3879 if (points.empty()) {
3880 _nothing_to_drag = true;
3884 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3888 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3890 if (_nothing_to_drag) {
3894 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3896 /* we are ignoring x position for this drag, so we can just pass in anything */
3897 _line->drag_motion (0, f, true, false);
3901 AutomationRangeDrag::finished (GdkEvent* event, bool)
3903 if (_nothing_to_drag) {
3907 motion (event, false);
3909 _line->clear_always_in_view ();
3913 AutomationRangeDrag::aborted ()
3915 _line->clear_always_in_view ();
3919 DraggingView::DraggingView (RegionView* v)
3922 initial_y = v->get_canvas_group()->property_y ();