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;
1743 /* snap modifier works differently here..
1744 its current state has to be passed to the
1745 various trim functions in order to work properly
1749 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1750 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1751 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1753 if (tv && tv->is_track()) {
1754 speed = tv->track()->speed();
1757 nframes64_t const pf = adjusted_current_frame (event);
1759 if (last_pointer_frame() > pf) {
1760 left_direction = true;
1762 left_direction = false;
1769 switch (_operation) {
1771 trim_type = "Region start trim";
1774 trim_type = "Region end trim";
1777 trim_type = "Region content trim";
1781 _editor->begin_reversible_command (trim_type);
1782 _have_transaction = true;
1784 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1785 RegionView* rv = i->view;
1786 rv->fake_set_opaque(false);
1787 rv->region()->clear_history ();
1788 rv->region()->suspend_property_changes ();
1790 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1793 arv->temporarily_hide_envelope ();
1796 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1797 insert_result = _editor->motion_frozen_playlists.insert (pl);
1799 if (insert_result.second) {
1805 if (left_direction) {
1806 frame_delta = (last_pointer_frame() - pf);
1808 frame_delta = (pf - last_pointer_frame());
1811 bool non_overlap_trim = false;
1813 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1814 non_overlap_trim = true;
1817 switch (_operation) {
1819 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1823 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1824 _editor->single_start_trim (*i->view, frame_delta, left_direction, non_overlap_trim);
1830 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1834 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1835 _editor->single_end_trim (*i->view, frame_delta, left_direction, non_overlap_trim);
1842 bool swap_direction = false;
1844 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1845 swap_direction = true;
1848 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1849 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1855 switch (_operation) {
1857 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1860 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1863 _editor->show_verbose_time_cursor (pf, 10);
1870 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1872 if (movement_occurred) {
1873 motion (event, false);
1875 if (!_editor->selection->selected (_primary)) {
1876 _editor->thaw_region_after_trim (*_primary);
1879 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1880 _editor->thaw_region_after_trim (*i->view);
1881 i->view->fake_set_opaque (true);
1882 if (_have_transaction) {
1883 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1887 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1891 _editor->motion_frozen_playlists.clear ();
1893 if (_have_transaction) {
1894 _editor->commit_reversible_command();
1898 /* no mouse movement */
1899 _editor->point_trim (event, adjusted_current_frame (event));
1904 TrimDrag::aborted ()
1906 /* Our motion method is changing model state, so use the Undo system
1907 to cancel. Perhaps not ideal, as this will leave an Undo point
1908 behind which may be slightly odd from the user's point of view.
1913 if (_have_transaction) {
1918 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1922 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1927 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1930 // create a dummy marker for visual representation of moving the copy.
1931 // The actual copying is not done before we reach the finish callback.
1933 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1934 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1935 *new MeterSection (_marker->meter()));
1937 _item = &new_marker->the_item ();
1938 _marker = new_marker;
1942 MetricSection& section (_marker->meter());
1944 if (!section.movable()) {
1950 Drag::start_grab (event, cursor);
1952 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1954 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1958 MeterMarkerDrag::motion (GdkEvent* event, bool)
1960 nframes64_t const pf = adjusted_current_frame (event);
1962 _marker->set_position (pf);
1964 _editor->show_verbose_time_cursor (pf, 10);
1968 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1970 if (!movement_occurred) {
1974 motion (event, false);
1978 TempoMap& map (_editor->session()->tempo_map());
1979 map.bbt_time (last_pointer_frame(), when);
1981 if (_copy == true) {
1982 _editor->begin_reversible_command (_("copy meter mark"));
1983 XMLNode &before = map.get_state();
1984 map.add_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 ();
1989 // delete the dummy marker we used for visual representation of copying.
1990 // a new visual marker will show up automatically.
1993 _editor->begin_reversible_command (_("move meter mark"));
1994 XMLNode &before = map.get_state();
1995 map.move_meter (_marker->meter(), when);
1996 XMLNode &after = map.get_state();
1997 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1998 _editor->commit_reversible_command ();
2003 MeterMarkerDrag::aborted ()
2005 _marker->set_position (_marker->meter().frame ());
2008 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2012 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2017 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2022 // create a dummy marker for visual representation of moving the copy.
2023 // The actual copying is not done before we reach the finish callback.
2025 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2026 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2027 *new TempoSection (_marker->tempo()));
2029 _item = &new_marker->the_item ();
2030 _marker = new_marker;
2034 MetricSection& section (_marker->tempo());
2036 if (!section.movable()) {
2041 Drag::start_grab (event, cursor);
2043 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2044 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2048 TempoMarkerDrag::motion (GdkEvent* event, bool)
2050 nframes64_t const pf = adjusted_current_frame (event);
2051 _marker->set_position (pf);
2052 _editor->show_verbose_time_cursor (pf, 10);
2056 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2058 if (!movement_occurred) {
2062 motion (event, false);
2066 TempoMap& map (_editor->session()->tempo_map());
2067 map.bbt_time (last_pointer_frame(), when);
2069 if (_copy == true) {
2070 _editor->begin_reversible_command (_("copy tempo mark"));
2071 XMLNode &before = map.get_state();
2072 map.add_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 ();
2077 // delete the dummy marker we used for visual representation of copying.
2078 // a new visual marker will show up automatically.
2081 _editor->begin_reversible_command (_("move tempo mark"));
2082 XMLNode &before = map.get_state();
2083 map.move_tempo (_marker->tempo(), when);
2084 XMLNode &after = map.get_state();
2085 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2086 _editor->commit_reversible_command ();
2091 TempoMarkerDrag::aborted ()
2093 _marker->set_position (_marker->tempo().frame());
2096 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2100 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2105 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2107 Drag::start_grab (event, c);
2111 nframes64_t where = _editor->event_frame (event, 0, 0);
2113 _editor->snap_to_with_modifier (where, event);
2114 _editor->playhead_cursor->set_position (where);
2118 if (_cursor == _editor->playhead_cursor) {
2119 _editor->_dragging_playhead = true;
2121 if (_editor->session() && _was_rolling && _stop) {
2122 _editor->session()->request_stop ();
2125 if (_editor->session() && _editor->session()->is_auditioning()) {
2126 _editor->session()->cancel_audition ();
2130 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2132 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2136 CursorDrag::motion (GdkEvent* event, bool)
2138 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2140 if (adjusted_frame == last_pointer_frame()) {
2144 _cursor->set_position (adjusted_frame);
2146 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2149 _editor->update_canvas_now ();
2151 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2155 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2157 _editor->_dragging_playhead = false;
2159 if (!movement_occurred && _stop) {
2163 motion (event, false);
2165 if (_item == &_editor->playhead_cursor->canvas_item) {
2166 if (_editor->session()) {
2167 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2168 _editor->_pending_locate_request = true;
2174 CursorDrag::aborted ()
2176 _editor->_dragging_playhead = false;
2177 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2180 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2181 : RegionDrag (e, i, p, v)
2187 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2189 Drag::start_grab (event, cursor);
2191 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2192 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2194 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2195 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2199 FadeInDrag::motion (GdkEvent* event, bool)
2201 nframes64_t fade_length;
2203 nframes64_t const pos = adjusted_current_frame (event);
2205 boost::shared_ptr<Region> region = _primary->region ();
2207 if (pos < (region->position() + 64)) {
2208 fade_length = 64; // this should be a minimum defined somewhere
2209 } else if (pos > region->last_frame()) {
2210 fade_length = region->length();
2212 fade_length = pos - region->position();
2215 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2217 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2223 tmp->reset_fade_in_shape_width (fade_length);
2226 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2230 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2232 if (!movement_occurred) {
2236 nframes64_t fade_length;
2238 nframes64_t const pos = adjusted_current_frame (event);
2240 boost::shared_ptr<Region> region = _primary->region ();
2242 if (pos < (region->position() + 64)) {
2243 fade_length = 64; // this should be a minimum defined somewhere
2244 } else if (pos > region->last_frame()) {
2245 fade_length = region->length();
2247 fade_length = pos - region->position();
2250 _editor->begin_reversible_command (_("change fade in length"));
2252 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2254 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2260 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2261 XMLNode &before = alist->get_state();
2263 tmp->audio_region()->set_fade_in_length (fade_length);
2264 tmp->audio_region()->set_fade_in_active (true);
2266 XMLNode &after = alist->get_state();
2267 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2270 _editor->commit_reversible_command ();
2274 FadeInDrag::aborted ()
2276 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2277 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2283 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2287 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2288 : RegionDrag (e, i, p, v)
2294 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2296 Drag::start_grab (event, cursor);
2298 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2299 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2301 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2302 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2306 FadeOutDrag::motion (GdkEvent* event, bool)
2308 nframes64_t fade_length;
2310 nframes64_t const pos = adjusted_current_frame (event);
2312 boost::shared_ptr<Region> region = _primary->region ();
2314 if (pos > (region->last_frame() - 64)) {
2315 fade_length = 64; // this should really be a minimum fade defined somewhere
2317 else if (pos < region->position()) {
2318 fade_length = region->length();
2321 fade_length = region->last_frame() - pos;
2324 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2326 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2332 tmp->reset_fade_out_shape_width (fade_length);
2335 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2339 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2341 if (!movement_occurred) {
2345 nframes64_t fade_length;
2347 nframes64_t const pos = adjusted_current_frame (event);
2349 boost::shared_ptr<Region> region = _primary->region ();
2351 if (pos > (region->last_frame() - 64)) {
2352 fade_length = 64; // this should really be a minimum fade defined somewhere
2354 else if (pos < region->position()) {
2355 fade_length = region->length();
2358 fade_length = region->last_frame() - pos;
2361 _editor->begin_reversible_command (_("change fade out length"));
2363 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2365 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2371 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2372 XMLNode &before = alist->get_state();
2374 tmp->audio_region()->set_fade_out_length (fade_length);
2375 tmp->audio_region()->set_fade_out_active (true);
2377 XMLNode &after = alist->get_state();
2378 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2381 _editor->commit_reversible_command ();
2385 FadeOutDrag::aborted ()
2387 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2388 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2394 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2398 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2401 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2404 _points.push_back (Gnome::Art::Point (0, 0));
2405 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2407 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2408 _line->property_width_pixels() = 1;
2409 _line->property_points () = _points;
2412 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2415 MarkerDrag::~MarkerDrag ()
2417 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2423 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2425 Drag::start_grab (event, cursor);
2429 Location *location = _editor->find_location_from_marker (_marker, is_start);
2430 _editor->_dragging_edit_point = true;
2432 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2434 update_item (location);
2436 // _drag_line->show();
2437 // _line->raise_to_top();
2440 _editor->show_verbose_time_cursor (location->start(), 10);
2442 _editor->show_verbose_time_cursor (location->end(), 10);
2445 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2448 case Selection::Toggle:
2449 _editor->selection->toggle (_marker);
2451 case Selection::Set:
2452 if (!_editor->selection->selected (_marker)) {
2453 _editor->selection->set (_marker);
2456 case Selection::Extend:
2458 Locations::LocationList ll;
2459 list<Marker*> to_add;
2461 _editor->selection->markers.range (s, e);
2462 s = min (_marker->position(), s);
2463 e = max (_marker->position(), e);
2466 if (e < max_frames) {
2469 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2470 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2471 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2474 to_add.push_back (lm->start);
2477 to_add.push_back (lm->end);
2481 if (!to_add.empty()) {
2482 _editor->selection->add (to_add);
2486 case Selection::Add:
2487 _editor->selection->add (_marker);
2491 /* Set up copies for us to manipulate during the drag */
2493 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2494 Location* l = _editor->find_location_from_marker (*i, is_start);
2495 _copied_locations.push_back (new Location (*l));
2500 MarkerDrag::motion (GdkEvent* event, bool)
2502 nframes64_t f_delta = 0;
2504 bool move_both = false;
2506 Location *real_location;
2507 Location *copy_location = 0;
2509 nframes64_t const newframe = adjusted_current_frame (event);
2511 nframes64_t next = newframe;
2513 if (newframe == last_pointer_frame()) {
2517 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2521 MarkerSelection::iterator i;
2522 list<Location*>::iterator x;
2524 /* find the marker we're dragging, and compute the delta */
2526 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2527 x != _copied_locations.end() && i != _editor->selection->markers.end();
2533 if (marker == _marker) {
2535 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2540 if (real_location->is_mark()) {
2541 f_delta = newframe - copy_location->start();
2545 switch (marker->type()) {
2547 case Marker::LoopStart:
2548 case Marker::PunchIn:
2549 f_delta = newframe - copy_location->start();
2553 case Marker::LoopEnd:
2554 case Marker::PunchOut:
2555 f_delta = newframe - copy_location->end();
2558 /* what kind of marker is this ? */
2566 if (i == _editor->selection->markers.end()) {
2567 /* hmm, impossible - we didn't find the dragged marker */
2571 /* now move them all */
2573 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2574 x != _copied_locations.end() && i != _editor->selection->markers.end();
2580 /* call this to find out if its the start or end */
2582 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2586 if (real_location->locked()) {
2590 if (copy_location->is_mark()) {
2594 copy_location->set_start (copy_location->start() + f_delta);
2598 nframes64_t new_start = copy_location->start() + f_delta;
2599 nframes64_t new_end = copy_location->end() + f_delta;
2601 if (is_start) { // start-of-range marker
2604 copy_location->set_start (new_start);
2605 copy_location->set_end (new_end);
2606 } else if (new_start < copy_location->end()) {
2607 copy_location->set_start (new_start);
2609 _editor->snap_to (next, 1, true);
2610 copy_location->set_end (next);
2611 copy_location->set_start (newframe);
2614 } else { // end marker
2617 copy_location->set_end (new_end);
2618 copy_location->set_start (new_start);
2619 } else if (new_end > copy_location->start()) {
2620 copy_location->set_end (new_end);
2621 } else if (newframe > 0) {
2622 _editor->snap_to (next, -1, true);
2623 copy_location->set_start (next);
2624 copy_location->set_end (newframe);
2629 update_item (copy_location);
2631 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2634 lm->set_position (copy_location->start(), copy_location->end());
2638 assert (!_copied_locations.empty());
2640 _editor->show_verbose_time_cursor (newframe, 10);
2643 _editor->update_canvas_now ();
2648 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2650 if (!movement_occurred) {
2652 /* just a click, do nothing but finish
2653 off the selection process
2656 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2659 case Selection::Set:
2660 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2661 _editor->selection->set (_marker);
2665 case Selection::Toggle:
2666 case Selection::Extend:
2667 case Selection::Add:
2674 _editor->_dragging_edit_point = false;
2676 _editor->begin_reversible_command ( _("move marker") );
2677 XMLNode &before = _editor->session()->locations()->get_state();
2679 MarkerSelection::iterator i;
2680 list<Location*>::iterator x;
2683 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2684 x != _copied_locations.end() && i != _editor->selection->markers.end();
2687 Location * location = _editor->find_location_from_marker (*i, is_start);
2691 if (location->locked()) {
2695 if (location->is_mark()) {
2696 location->set_start ((*x)->start());
2698 location->set ((*x)->start(), (*x)->end());
2703 XMLNode &after = _editor->session()->locations()->get_state();
2704 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2705 _editor->commit_reversible_command ();
2711 MarkerDrag::aborted ()
2717 MarkerDrag::update_item (Location* location)
2719 double const x1 = _editor->frame_to_pixel (location->start());
2721 _points.front().set_x(x1);
2722 _points.back().set_x(x1);
2723 _line->property_points() = _points;
2726 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2728 _cumulative_x_drag (0),
2729 _cumulative_y_drag (0)
2731 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2737 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2739 Drag::start_grab (event, _editor->fader_cursor);
2741 // start the grab at the center of the control point so
2742 // the point doesn't 'jump' to the mouse after the first drag
2743 _time_axis_view_grab_x = _point->get_x();
2744 _time_axis_view_grab_y = _point->get_y();
2746 float const fraction = 1 - (_point->get_y() / _point->line().height());
2748 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2750 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2751 event->button.x + 10, event->button.y + 10);
2753 _editor->show_verbose_canvas_cursor ();
2757 ControlPointDrag::motion (GdkEvent* event, bool)
2759 double dx = _drags->current_pointer_x() - last_pointer_x();
2760 double dy = _drags->current_pointer_y() - last_pointer_y();
2762 if (event->button.state & Keyboard::SecondaryModifier) {
2767 /* coordinate in TimeAxisView's space */
2768 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2769 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2771 // calculate zero crossing point. back off by .01 to stay on the
2772 // positive side of zero
2773 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2775 // make sure we hit zero when passing through
2776 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2780 if (_x_constrained) {
2781 cx = _time_axis_view_grab_x;
2783 if (_y_constrained) {
2784 cy = _time_axis_view_grab_y;
2787 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2788 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2792 cy = min ((double) _point->line().height(), cy);
2794 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2796 if (!_x_constrained) {
2797 _editor->snap_to_with_modifier (cx_frames, event);
2800 float const fraction = 1.0 - (cy / _point->line().height());
2802 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2804 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2806 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2810 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2812 if (!movement_occurred) {
2816 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2817 _editor->reset_point_selection ();
2821 motion (event, false);
2823 _point->line().end_drag ();
2827 ControlPointDrag::aborted ()
2829 _point->line().reset ();
2833 ControlPointDrag::active (Editing::MouseMode m)
2835 if (m == Editing::MouseGain) {
2836 /* always active in mouse gain */
2840 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2841 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2844 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2847 _cumulative_y_drag (0)
2852 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2854 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2857 _item = &_line->grab_item ();
2859 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2860 origin, and ditto for y.
2863 double cx = event->button.x;
2864 double cy = event->button.y;
2866 _line->parent_group().w2i (cx, cy);
2868 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2873 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2874 /* no adjacent points */
2878 Drag::start_grab (event, _editor->fader_cursor);
2880 /* store grab start in parent frame */
2882 _time_axis_view_grab_x = cx;
2883 _time_axis_view_grab_y = cy;
2885 double fraction = 1.0 - (cy / _line->height());
2887 _line->start_drag_line (before, after, fraction);
2889 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2890 event->button.x + 10, event->button.y + 10);
2892 _editor->show_verbose_canvas_cursor ();
2896 LineDrag::motion (GdkEvent* event, bool)
2898 double dy = _drags->current_pointer_y() - last_pointer_y();
2900 if (event->button.state & Keyboard::SecondaryModifier) {
2904 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2906 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2909 cy = min ((double) _line->height(), cy);
2911 double const fraction = 1.0 - (cy / _line->height());
2915 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2921 /* we are ignoring x position for this drag, so we can just pass in anything */
2922 _line->drag_motion (0, fraction, true, push);
2924 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2928 LineDrag::finished (GdkEvent* event, bool)
2930 motion (event, false);
2935 LineDrag::aborted ()
2941 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2943 Drag::start_grab (event);
2944 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2948 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2955 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2957 nframes64_t grab = grab_frame ();
2958 if (Config->get_rubberbanding_snaps_to_grid ()) {
2959 _editor->snap_to_with_modifier (grab, event);
2962 /* base start and end on initial click position */
2972 if (_drags->current_pointer_y() < grab_y()) {
2973 y1 = _drags->current_pointer_y();
2976 y2 = _drags->current_pointer_y();
2981 if (start != end || y1 != y2) {
2983 double x1 = _editor->frame_to_pixel (start);
2984 double x2 = _editor->frame_to_pixel (end);
2986 _editor->rubberband_rect->property_x1() = x1;
2987 _editor->rubberband_rect->property_y1() = y1;
2988 _editor->rubberband_rect->property_x2() = x2;
2989 _editor->rubberband_rect->property_y2() = y2;
2991 _editor->rubberband_rect->show();
2992 _editor->rubberband_rect->raise_to_top();
2994 _editor->show_verbose_time_cursor (pf, 10);
2999 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3001 if (movement_occurred) {
3003 motion (event, false);
3006 if (_drags->current_pointer_y() < grab_y()) {
3007 y1 = _drags->current_pointer_y();
3010 y2 = _drags->current_pointer_y();
3015 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3018 _editor->begin_reversible_command (_("rubberband selection"));
3020 if (grab_frame() < last_pointer_frame()) {
3021 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3023 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3027 _editor->commit_reversible_command ();
3031 if (!getenv("ARDOUR_SAE")) {
3032 _editor->selection->clear_tracks();
3034 _editor->selection->clear_regions();
3035 _editor->selection->clear_points ();
3036 _editor->selection->clear_lines ();
3039 _editor->rubberband_rect->hide();
3043 RubberbandSelectDrag::aborted ()
3045 _editor->rubberband_rect->hide ();
3049 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3051 Drag::start_grab (event);
3053 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3057 TimeFXDrag::motion (GdkEvent* event, bool)
3059 RegionView* rv = _primary;
3061 nframes64_t const pf = adjusted_current_frame (event);
3063 if (pf > rv->region()->position()) {
3064 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3067 _editor->show_verbose_time_cursor (pf, 10);
3071 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3073 _primary->get_time_axis_view().hide_timestretch ();
3075 if (!movement_occurred) {
3079 if (last_pointer_frame() < _primary->region()->position()) {
3080 /* backwards drag of the left edge - not usable */
3084 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3086 float percentage = (double) newlen / (double) _primary->region()->length();
3088 #ifndef USE_RUBBERBAND
3089 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3090 if (_primary->region()->data_type() == DataType::AUDIO) {
3091 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3095 _editor->begin_reversible_command (_("timestretch"));
3097 // XXX how do timeFX on multiple regions ?
3102 if (_editor->time_stretch (rs, percentage) == -1) {
3103 error << _("An error occurred while executing time stretch operation") << endmsg;
3108 TimeFXDrag::aborted ()
3110 _primary->get_time_axis_view().hide_timestretch ();
3115 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3117 Drag::start_grab (event);
3121 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3123 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3127 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3129 if (movement_occurred && _editor->session()) {
3130 /* make sure we stop */
3131 _editor->session()->request_transport_speed (0.0);
3136 ScrubDrag::aborted ()
3141 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3145 , _original_pointer_time_axis (-1)
3146 , _last_pointer_time_axis (-1)
3152 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3154 nframes64_t start = 0;
3155 nframes64_t end = 0;
3157 if (_editor->session() == 0) {
3161 Gdk::Cursor* cursor = 0;
3163 switch (_operation) {
3164 case CreateSelection:
3165 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3170 cursor = _editor->selector_cursor;
3171 Drag::start_grab (event, cursor);
3174 case SelectionStartTrim:
3175 if (_editor->clicked_axisview) {
3176 _editor->clicked_axisview->order_selection_trims (_item, true);
3178 Drag::start_grab (event, _editor->trimmer_cursor);
3179 start = _editor->selection->time[_editor->clicked_selection].start;
3180 _pointer_frame_offset = grab_frame() - start;
3183 case SelectionEndTrim:
3184 if (_editor->clicked_axisview) {
3185 _editor->clicked_axisview->order_selection_trims (_item, false);
3187 Drag::start_grab (event, _editor->trimmer_cursor);
3188 end = _editor->selection->time[_editor->clicked_selection].end;
3189 _pointer_frame_offset = grab_frame() - end;
3193 start = _editor->selection->time[_editor->clicked_selection].start;
3194 Drag::start_grab (event, cursor);
3195 _pointer_frame_offset = grab_frame() - start;
3199 if (_operation == SelectionMove) {
3200 _editor->show_verbose_time_cursor (start, 10);
3202 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3205 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3209 SelectionDrag::motion (GdkEvent* event, bool first_move)
3211 nframes64_t start = 0;
3212 nframes64_t end = 0;
3215 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3216 if (pending_time_axis.first == 0) {
3220 nframes64_t const pending_position = adjusted_current_frame (event);
3222 /* only alter selection if things have changed */
3224 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3228 switch (_operation) {
3229 case CreateSelection:
3231 nframes64_t grab = grab_frame ();
3234 _editor->snap_to (grab);
3237 if (pending_position < grab_frame()) {
3238 start = pending_position;
3241 end = pending_position;
3245 /* first drag: Either add to the selection
3246 or create a new selection
3252 /* adding to the selection */
3253 _editor->selection->add (_editor->clicked_axisview);
3254 _editor->clicked_selection = _editor->selection->add (start, end);
3259 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3260 _editor->selection->set (_editor->clicked_axisview);
3263 _editor->clicked_selection = _editor->selection->set (start, end);
3267 /* select the track that we're in */
3268 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3269 _editor->selection->add (pending_time_axis.first);
3270 _added_time_axes.push_back (pending_time_axis.first);
3273 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3274 tracks that we selected in the first place.
3277 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3278 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3280 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3281 while (i != _added_time_axes.end()) {
3283 list<TimeAxisView*>::iterator tmp = i;
3286 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3287 _editor->selection->remove (*i);
3288 _added_time_axes.remove (*i);
3297 case SelectionStartTrim:
3299 start = _editor->selection->time[_editor->clicked_selection].start;
3300 end = _editor->selection->time[_editor->clicked_selection].end;
3302 if (pending_position > end) {
3305 start = pending_position;
3309 case SelectionEndTrim:
3311 start = _editor->selection->time[_editor->clicked_selection].start;
3312 end = _editor->selection->time[_editor->clicked_selection].end;
3314 if (pending_position < start) {
3317 end = pending_position;
3324 start = _editor->selection->time[_editor->clicked_selection].start;
3325 end = _editor->selection->time[_editor->clicked_selection].end;
3327 length = end - start;
3329 start = pending_position;
3330 _editor->snap_to (start);
3332 end = start + length;
3337 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3338 _editor->start_canvas_autoscroll (1, 0);
3342 _editor->selection->replace (_editor->clicked_selection, start, end);
3345 if (_operation == SelectionMove) {
3346 _editor->show_verbose_time_cursor(start, 10);
3348 _editor->show_verbose_time_cursor(pending_position, 10);
3353 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3355 Session* s = _editor->session();
3357 if (movement_occurred) {
3358 motion (event, false);
3359 /* XXX this is not object-oriented programming at all. ick */
3360 if (_editor->selection->time.consolidate()) {
3361 _editor->selection->TimeChanged ();
3364 /* XXX what if its a music time selection? */
3365 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3366 s->request_play_range (&_editor->selection->time, true);
3371 /* just a click, no pointer movement.*/
3373 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3374 _editor->selection->clear_time();
3377 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3378 _editor->selection->set (_editor->clicked_axisview);
3381 if (s && s->get_play_range () && s->transport_rolling()) {
3382 s->request_stop (false, false);
3387 _editor->stop_canvas_autoscroll ();
3391 SelectionDrag::aborted ()
3396 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3401 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3402 _drag_rect->hide ();
3404 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3405 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3409 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3411 if (_editor->session() == 0) {
3415 Gdk::Cursor* cursor = 0;
3417 if (!_editor->temp_location) {
3418 _editor->temp_location = new Location;
3421 switch (_operation) {
3422 case CreateRangeMarker:
3423 case CreateTransportMarker:
3424 case CreateCDMarker:
3426 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3431 cursor = _editor->selector_cursor;
3435 Drag::start_grab (event, cursor);
3437 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3441 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3443 nframes64_t start = 0;
3444 nframes64_t end = 0;
3445 ArdourCanvas::SimpleRect *crect;
3447 switch (_operation) {
3448 case CreateRangeMarker:
3449 crect = _editor->range_bar_drag_rect;
3451 case CreateTransportMarker:
3452 crect = _editor->transport_bar_drag_rect;
3454 case CreateCDMarker:
3455 crect = _editor->cd_marker_bar_drag_rect;
3458 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3463 nframes64_t const pf = adjusted_current_frame (event);
3465 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3466 nframes64_t grab = grab_frame ();
3467 _editor->snap_to (grab);
3469 if (pf < grab_frame()) {
3477 /* first drag: Either add to the selection
3478 or create a new selection.
3483 _editor->temp_location->set (start, end);
3487 update_item (_editor->temp_location);
3489 //_drag_rect->raise_to_top();
3494 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3495 _editor->start_canvas_autoscroll (1, 0);
3499 _editor->temp_location->set (start, end);
3501 double x1 = _editor->frame_to_pixel (start);
3502 double x2 = _editor->frame_to_pixel (end);
3503 crect->property_x1() = x1;
3504 crect->property_x2() = x2;
3506 update_item (_editor->temp_location);
3509 _editor->show_verbose_time_cursor (pf, 10);
3514 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3516 Location * newloc = 0;
3520 if (movement_occurred) {
3521 motion (event, false);
3524 switch (_operation) {
3525 case CreateRangeMarker:
3526 case CreateCDMarker:
3528 _editor->begin_reversible_command (_("new range marker"));
3529 XMLNode &before = _editor->session()->locations()->get_state();
3530 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3531 if (_operation == CreateCDMarker) {
3532 flags = Location::IsRangeMarker | Location::IsCDMarker;
3533 _editor->cd_marker_bar_drag_rect->hide();
3536 flags = Location::IsRangeMarker;
3537 _editor->range_bar_drag_rect->hide();
3539 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3540 _editor->session()->locations()->add (newloc, true);
3541 XMLNode &after = _editor->session()->locations()->get_state();
3542 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3543 _editor->commit_reversible_command ();
3547 case CreateTransportMarker:
3548 // popup menu to pick loop or punch
3549 _editor->new_transport_marker_context_menu (&event->button, _item);
3553 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3555 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3560 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3562 if (end == max_frames) {
3563 end = _editor->session()->current_end_frame ();
3566 if (start == max_frames) {
3567 start = _editor->session()->current_start_frame ();
3570 switch (_editor->mouse_mode) {
3572 /* find the two markers on either side and then make the selection from it */
3573 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3577 /* find the two markers on either side of the click and make the range out of it */
3578 _editor->selection->set (start, end);
3587 _editor->stop_canvas_autoscroll ();
3591 RangeMarkerBarDrag::aborted ()
3597 RangeMarkerBarDrag::update_item (Location* location)
3599 double const x1 = _editor->frame_to_pixel (location->start());
3600 double const x2 = _editor->frame_to_pixel (location->end());
3602 _drag_rect->property_x1() = x1;
3603 _drag_rect->property_x2() = x2;
3607 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3609 Drag::start_grab (event, _editor->zoom_cursor);
3610 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3614 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3619 nframes64_t const pf = adjusted_current_frame (event);
3621 nframes64_t grab = grab_frame ();
3622 _editor->snap_to_with_modifier (grab, event);
3624 /* base start and end on initial click position */
3636 _editor->zoom_rect->show();
3637 _editor->zoom_rect->raise_to_top();
3640 _editor->reposition_zoom_rect(start, end);
3642 _editor->show_verbose_time_cursor (pf, 10);
3647 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3649 if (movement_occurred) {
3650 motion (event, false);
3652 if (grab_frame() < last_pointer_frame()) {
3653 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3655 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3658 _editor->temporal_zoom_to_frame (false, grab_frame());
3660 temporal_zoom_step (false);
3661 center_screen (grab_frame());
3665 _editor->zoom_rect->hide();
3669 MouseZoomDrag::aborted ()
3671 _editor->zoom_rect->hide ();
3674 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3677 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3678 region = &cnote->region_view();
3682 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3684 Drag::start_grab (event);
3687 drag_delta_note = 0;
3692 event_x = _drags->current_pointer_x();
3693 event_y = _drags->current_pointer_y();
3695 _item->property_parent().get_value()->w2i(event_x, event_y);
3697 last_x = region->snap_to_pixel(event_x);
3700 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3702 if (!(was_selected = cnote->selected())) {
3704 /* tertiary-click means extend selection - we'll do that on button release,
3705 so don't add it here, because otherwise we make it hard to figure
3706 out the "extend-to" range.
3709 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3712 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3715 region->note_selected (cnote, true);
3717 region->unique_select (cnote);
3724 NoteDrag::motion (GdkEvent*, bool)
3726 MidiStreamView* streamview = region->midi_stream_view();
3730 event_x = _drags->current_pointer_x();
3731 event_y = _drags->current_pointer_y();
3733 _item->property_parent().get_value()->w2i(event_x, event_y);
3735 event_x = region->snap_to_pixel(event_x);
3737 double dx = event_x - last_x;
3738 double dy = event_y - last_y;
3743 // Snap to note rows
3745 if (abs (dy) < streamview->note_height()) {
3748 int8_t this_delta_note;
3750 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3752 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3754 drag_delta_note -= this_delta_note;
3755 dy = streamview->note_height() * this_delta_note;
3756 last_y = last_y + dy;
3760 region->move_selection (dx, dy);
3762 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3764 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3765 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3766 _editor->show_verbose_canvas_cursor_with (buf);
3771 NoteDrag::finished (GdkEvent* ev, bool moved)
3773 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3776 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3779 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3781 region->note_deselected (cnote);
3784 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3785 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3787 if (!extend && !add && region->selection_size() > 1) {
3788 region->unique_select(cnote);
3789 } else if (extend) {
3790 region->note_selected (cnote, true, true);
3792 /* it was added during button press */
3797 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3802 NoteDrag::aborted ()
3807 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3810 , _nothing_to_drag (false)
3812 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3815 _line = _atav->line ();
3819 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3821 Drag::start_grab (event, cursor);
3823 list<ControlPoint*> points;
3825 XMLNode* state = &_line->get_state ();
3827 if (_ranges.empty()) {
3829 uint32_t const N = _line->npoints ();
3830 for (uint32_t i = 0; i < N; ++i) {
3831 points.push_back (_line->nth (i));
3836 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3837 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3839 /* fade into and out of the region that we're dragging;
3840 64 samples length plucked out of thin air.
3842 nframes64_t const h = (j->start + j->end) / 2;
3843 nframes64_t a = j->start + 64;
3847 nframes64_t b = j->end - 64;
3852 the_list->add (j->start, the_list->eval (j->start));
3853 _line->add_always_in_view (j->start);
3854 the_list->add (a, the_list->eval (a));
3855 _line->add_always_in_view (a);
3856 the_list->add (b, the_list->eval (b));
3857 _line->add_always_in_view (b);
3858 the_list->add (j->end, the_list->eval (j->end));
3859 _line->add_always_in_view (j->end);
3862 uint32_t const N = _line->npoints ();
3863 for (uint32_t i = 0; i < N; ++i) {
3865 ControlPoint* p = _line->nth (i);
3867 list<AudioRange>::const_iterator j = _ranges.begin ();
3868 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3872 if (j != _ranges.end()) {
3873 points.push_back (p);
3878 if (points.empty()) {
3879 _nothing_to_drag = true;
3883 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3887 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3889 if (_nothing_to_drag) {
3893 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3895 /* we are ignoring x position for this drag, so we can just pass in anything */
3896 _line->drag_motion (0, f, true, false);
3900 AutomationRangeDrag::finished (GdkEvent* event, bool)
3902 if (_nothing_to_drag) {
3906 motion (event, false);
3908 _line->clear_always_in_view ();
3912 AutomationRangeDrag::aborted ()
3914 _line->clear_always_in_view ();
3918 DraggingView::DraggingView (RegionView* v)
3921 initial_y = v->get_canvas_group()->property_y ();