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 "ardour/diskstream.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
28 #include "ardour/midi_diskstream.h"
32 #include "audio_region_view.h"
33 #include "midi_region_view.h"
34 #include "ardour_ui.h"
35 #include "gui_thread.h"
36 #include "control_point.h"
38 #include "region_gain_line.h"
39 #include "editor_drag.h"
40 #include "audio_time_axis.h"
41 #include "midi_time_axis.h"
42 #include "canvas-note.h"
43 #include "selection.h"
44 #include "midi_selection.h"
45 #include "automation_time_axis.h"
48 using namespace ARDOUR;
51 using namespace Editing;
52 using namespace ArdourCanvas;
54 using Gtkmm2ext::Keyboard;
56 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
58 DragManager::DragManager (Editor* e)
61 , _current_pointer_frame (0)
66 DragManager::~DragManager ()
74 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
83 DragManager::break_drag ()
87 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
98 DragManager::add (Drag* d)
100 d->set_manager (this);
101 _drags.push_back (d);
105 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
107 assert (_drags.empty ());
108 d->set_manager (this);
109 _drags.push_back (d);
114 DragManager::start_grab (GdkEvent* e)
116 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
118 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
119 (*i)->start_grab (e);
124 DragManager::end_grab (GdkEvent* e)
129 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
130 bool const t = (*i)->end_grab (e);
145 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
149 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
151 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
152 bool const t = (*i)->motion_handler (e, from_autoscroll);
163 DragManager::have_item (ArdourCanvas::Item* i) const
165 list<Drag*>::const_iterator j = _drags.begin ();
166 while (j != _drags.end() && (*j)->item () != i) {
170 return j != _drags.end ();
173 pair<nframes64_t, nframes64_t>
174 DragManager::extent () const
176 if (_drags.empty()) {
177 return make_pair (0, 0);
180 list<Drag*>::const_iterator i = _drags.begin ();
181 pair<nframes64_t, nframes64_t> e = (*i)->extent ();
184 while (i != _drags.end()) {
185 pair<nframes64_t, nframes64_t> const t = (*i)->extent ();
186 e.first = min (e.first, t.first);
187 e.second = max (e.second, t.second);
194 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
197 , _pointer_frame_offset (0)
198 , _move_threshold_passed (false)
200 , _last_pointer_frame (0)
206 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
212 cursor = _editor->which_grabber_cursor ();
215 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
219 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
222 cursor = _editor->which_grabber_cursor ();
225 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
227 if (Keyboard::is_button2_event (&event->button)) {
228 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
229 _y_constrained = true;
230 _x_constrained = false;
232 _y_constrained = false;
233 _x_constrained = true;
236 _x_constrained = false;
237 _y_constrained = false;
240 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
241 _grab_frame = adjusted_frame (_grab_frame, event);
242 _last_pointer_frame = _grab_frame;
243 _last_pointer_x = _grab_x;
244 _last_pointer_y = _grab_y;
246 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
250 if (_editor->session() && _editor->session()->transport_rolling()) {
253 _was_rolling = false;
256 switch (_editor->snap_type()) {
257 case SnapToRegionStart:
258 case SnapToRegionEnd:
259 case SnapToRegionSync:
260 case SnapToRegionBoundary:
261 _editor->build_region_boundary_cache ();
268 /** @param event GDK event, or 0.
269 * @return true if some movement occurred, otherwise false.
272 Drag::end_grab (GdkEvent* event)
274 _editor->stop_canvas_autoscroll ();
276 _item->ungrab (event ? event->button.time : 0);
278 finished (event, _move_threshold_passed);
280 _editor->hide_verbose_canvas_cursor();
282 return _move_threshold_passed;
286 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
290 if (f > _pointer_frame_offset) {
291 pos = f - _pointer_frame_offset;
295 _editor->snap_to_with_modifier (pos, event);
302 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
304 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
308 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
310 /* check to see if we have moved in any way that matters since the last motion event */
311 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
312 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
316 pair<nframes64_t, int> const threshold = move_threshold ();
318 bool const old_move_threshold_passed = _move_threshold_passed;
320 if (!from_autoscroll && !_move_threshold_passed) {
322 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
323 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
325 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
328 if (active (_editor->mouse_mode) && _move_threshold_passed) {
330 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
331 if (!from_autoscroll) {
332 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
335 motion (event, _move_threshold_passed != old_move_threshold_passed);
337 _last_pointer_x = _drags->current_pointer_x ();
338 _last_pointer_y = _drags->current_pointer_y ();
339 _last_pointer_frame = adjusted_current_frame (event);
357 _editor->stop_canvas_autoscroll ();
358 _editor->hide_verbose_canvas_cursor ();
361 pair<nframes64_t, nframes64_t>
362 Drag::extent () const
364 nframes64_t const f = adjusted_current_frame (0);
365 return make_pair (f, f);
368 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
373 RegionView::RegionViewGoingAway.connect (death_connection, ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
377 RegionDrag::region_going_away (RegionView* v)
379 if (!_drags->ending ()) {
384 pair<nframes64_t, nframes64_t>
385 RegionDrag::extent () const
387 nframes64_t const f = adjusted_current_frame (0);
388 return make_pair (f, f + _primary->region()->length ());
392 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
393 : RegionDrag (e, i, p, v),
404 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
406 Drag::start_grab (event);
408 _editor->show_verbose_time_cursor (_last_frame_position, 10);
411 RegionMotionDrag::TimeAxisViewSummary
412 RegionMotionDrag::get_time_axis_view_summary ()
414 int32_t children = 0;
415 TimeAxisViewSummary sum;
417 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
419 /* get a bitmask representing the visible tracks */
421 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
422 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
423 TimeAxisView::Children children_list;
425 /* zeroes are audio/MIDI tracks. ones are other types. */
427 if (!rtv->hidden()) {
429 if (!rtv->is_track()) {
430 /* not an audio nor MIDI track */
431 sum.tracks = sum.tracks |= (0x01 << rtv->order());
434 sum.height_list[rtv->order()] = (*i)->current_height();
437 if ((children_list = rtv->get_child_list()).size() > 0) {
438 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
439 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
440 sum.height_list[rtv->order() + children] = (*j)->current_height();
451 RegionMotionDrag::compute_y_delta (
452 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
453 int32_t last_pointer_layer, int32_t current_pointer_layer,
454 TimeAxisViewSummary const & tavs,
455 int32_t* pointer_order_span, int32_t* pointer_layer_span,
456 int32_t* canvas_pointer_order_span
460 *pointer_order_span = 0;
461 *pointer_layer_span = 0;
465 bool clamp_y_axis = false;
467 /* the change in track order between this callback and the last */
468 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
469 /* the change in layer between this callback and the last;
470 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
471 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
473 if (*pointer_order_span != 0) {
475 /* find the actual pointer span, in terms of the number of visible tracks;
476 to do this, we reduce |pointer_order_span| by the number of hidden tracks
479 *canvas_pointer_order_span = *pointer_order_span;
480 if (last_pointer_view->order() >= current_pointer_view->order()) {
481 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
482 if (tavs.height_list[y] == 0) {
483 *canvas_pointer_order_span--;
487 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
488 if (tavs.height_list[y] == 0) {
489 *canvas_pointer_order_span++;
494 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
496 RegionView* rv = (*i);
498 if (rv->region()->locked()) {
502 double ix1, ix2, iy1, iy2;
503 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
504 rv->get_canvas_frame()->i2w (ix1, iy1);
505 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
507 /* get the new trackview for this particular region */
508 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
510 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
512 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
513 as surely this is a per-region thing... */
515 clamp_y_axis = y_movement_disallowed (
516 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
524 } else if (_dest_trackview == current_pointer_view) {
526 if (current_pointer_layer == last_pointer_layer) {
527 /* No movement; clamp */
533 _dest_trackview = current_pointer_view;
534 _dest_layer = current_pointer_layer;
542 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
544 /* compute the amount of pointer motion in frames, and where
545 the region would be if we moved it by that much.
547 *pending_region_position = adjusted_current_frame (event);
549 nframes64_t sync_frame;
550 nframes64_t sync_offset;
553 sync_offset = _primary->region()->sync_offset (sync_dir);
555 /* we don't handle a sync point that lies before zero.
557 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
559 sync_frame = *pending_region_position + (sync_dir*sync_offset);
561 _editor->snap_to_with_modifier (sync_frame, event);
563 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
566 *pending_region_position = _last_frame_position;
569 if (*pending_region_position > max_frames - _primary->region()->length()) {
570 *pending_region_position = _last_frame_position;
575 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
577 /* now compute the canvas unit distance we need to move the regionview
578 to make it appear at the new location.
581 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
583 if (*pending_region_position <= _last_frame_position) {
585 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
587 RegionView* rv = (*i);
589 // If any regionview is at zero, we need to know so we can stop further leftward motion.
591 double ix1, ix2, iy1, iy2;
592 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
593 rv->get_canvas_frame()->i2w (ix1, iy1);
595 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
597 *pending_region_position = _last_frame_position;
604 _last_frame_position = *pending_region_position;
611 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
615 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
617 vector<int32_t>::iterator j;
619 /* *pointer* variables reflect things about the pointer; as we may be moving
620 multiple regions, much detail must be computed per-region */
622 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
623 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
624 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
625 is always 0 regardless of what the region's "real" layer is */
626 RouteTimeAxisView* current_pointer_view;
627 layer_t current_pointer_layer;
628 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
632 /* TimeAxisView that we were pointing at last time we entered this method */
633 TimeAxisView const * const last_pointer_view = _dest_trackview;
634 /* the order of the track that we were pointing at last time we entered this method */
635 int32_t const last_pointer_order = last_pointer_view->order ();
636 /* the layer that we were pointing at last time we entered this method */
637 layer_t const last_pointer_layer = _dest_layer;
639 int32_t pointer_order_span;
640 int32_t pointer_layer_span;
641 int32_t canvas_pointer_order_span;
643 bool const clamp_y_axis = compute_y_delta (
644 last_pointer_view, current_pointer_view,
645 last_pointer_layer, current_pointer_layer, tavs,
646 &pointer_order_span, &pointer_layer_span,
647 &canvas_pointer_order_span
650 nframes64_t pending_region_position;
651 double const x_delta = compute_x_delta (event, &pending_region_position);
653 /*************************************************************
655 ************************************************************/
657 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
658 /* haven't reached next snap point, and we're not switching
659 trackviews nor layers. nothing to do.
664 /*************************************************************
666 ************************************************************/
668 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
670 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
672 RegionView* rv = (*i);
674 if (rv->region()->locked()) {
678 /* here we are calculating the y distance from the
679 top of the first track view to the top of the region
680 area of the track view that we're working on */
682 /* this x value is just a dummy value so that we have something
687 /* distance from the top of this track view to the region area
688 of our track view is always 1 */
692 /* convert to world coordinates, ie distance from the top of
695 rv->get_canvas_frame()->i2w (ix1, iy1);
697 /* compensate for the ruler section and the vertical scrollbar position */
698 iy1 += _editor->get_trackview_group_vertical_offset ();
702 // hide any dependent views
704 rv->get_time_axis_view().hide_dependent_views (*rv);
707 reparent to a non scrolling group so that we can keep the
708 region selection above all time axis views.
709 reparenting means we have to move the rv as the two
710 parent groups have different coordinates.
713 rv->get_canvas_group()->property_y() = iy1 - 1;
714 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
716 rv->fake_set_opaque (true);
719 /* current view for this particular region */
720 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
721 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
723 if (pointer_order_span != 0 && !clamp_y_axis) {
725 /* INTER-TRACK MOVEMENT */
727 /* move through the height list to the track that the region is currently on */
728 vector<int32_t>::iterator j = tavs.height_list.begin ();
730 while (j != tavs.height_list.end () && x != rtv->order ()) {
736 int32_t temp_pointer_order_span = canvas_pointer_order_span;
738 if (j != tavs.height_list.end ()) {
740 /* Account for layers in the original and
741 destination tracks. If we're moving around in layers we assume
742 that only one track is involved, so it's ok to use *pointer*
745 StreamView* lv = last_pointer_view->view ();
748 /* move to the top of the last trackview */
749 if (lv->layer_display () == Stacked) {
750 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
753 StreamView* cv = current_pointer_view->view ();
756 /* move to the right layer on the current trackview */
757 if (cv->layer_display () == Stacked) {
758 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
761 /* And for being on a non-topmost layer on the new
764 while (temp_pointer_order_span > 0) {
765 /* we're moving up canvas-wise,
766 so we need to find the next track height
768 if (j != tavs.height_list.begin()) {
772 if (x != last_pointer_order) {
774 ++temp_pointer_order_span;
779 temp_pointer_order_span--;
782 while (temp_pointer_order_span < 0) {
786 if (x != last_pointer_order) {
788 --temp_pointer_order_span;
792 if (j != tavs.height_list.end()) {
796 temp_pointer_order_span++;
800 /* find out where we'll be when we move and set height accordingly */
802 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
803 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
804 rv->set_height (temp_rtv->view()->child_height());
806 /* if you un-comment the following, the region colours will follow
807 the track colours whilst dragging; personally
808 i think this can confuse things, but never mind.
811 //const GdkColor& col (temp_rtv->view->get_region_color());
812 //rv->set_color (const_cast<GdkColor&>(col));
816 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
818 /* INTER-LAYER MOVEMENT in the same track */
819 y_delta = rtv->view()->child_height () * pointer_layer_span;
824 _editor->mouse_brush_insert_region (rv, pending_region_position);
826 rv->move (x_delta, y_delta);
829 } /* foreach region */
831 _total_x_delta += x_delta;
834 _editor->cursor_group->raise_to_top();
837 if (x_delta != 0 && !_brushing) {
838 _editor->show_verbose_time_cursor (_last_frame_position, 10);
843 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
845 if (_copy && first_move) {
846 copy_regions (event);
849 RegionMotionDrag::motion (event, first_move);
853 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
855 vector<RegionView*> copies;
856 boost::shared_ptr<Diskstream> ds;
857 boost::shared_ptr<Playlist> from_playlist;
858 RegionSelection new_views;
859 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
860 PlaylistSet modified_playlists;
861 PlaylistSet frozen_playlists;
862 list <sigc::connection> modified_playlist_connections;
863 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
864 nframes64_t drag_delta;
865 bool changed_tracks, changed_position;
866 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
867 RouteTimeAxisView* source_tv;
869 if (!movement_occurred) {
875 /* all changes were made during motion event handlers */
878 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
879 copies.push_back (*i);
886 /* reverse this here so that we have the correct logic to finalize
890 if (Config->get_edit_mode() == Lock) {
891 _x_constrained = !_x_constrained;
895 if (_x_constrained) {
896 _editor->begin_reversible_command (_("fixed time region copy"));
898 _editor->begin_reversible_command (_("region copy"));
901 if (_x_constrained) {
902 _editor->begin_reversible_command (_("fixed time region drag"));
904 _editor->begin_reversible_command (_("region drag"));
908 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
909 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
911 drag_delta = _primary->region()->position() - _last_frame_position;
913 _editor->update_canvas_now ();
915 /* make a list of where each region ended up */
916 final = find_time_axis_views_and_layers ();
918 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
920 RegionView* rv = (*i);
921 RouteTimeAxisView* dest_rtv = final[*i].first;
922 layer_t dest_layer = final[*i].second;
926 if (rv->region()->locked()) {
931 if (changed_position && !_x_constrained) {
932 where = rv->region()->position() - drag_delta;
934 where = rv->region()->position();
937 boost::shared_ptr<Region> new_region;
940 /* we already made a copy */
941 new_region = rv->region();
943 /* undo the previous hide_dependent_views so that xfades don't
944 disappear on copying regions
947 //rv->get_time_axis_view().reveal_dependent_views (*rv);
949 } else if (changed_tracks && dest_rtv->playlist()) {
950 new_region = RegionFactory::create (rv->region());
953 if (changed_tracks || _copy) {
955 boost::shared_ptr<Playlist> to_playlist = dest_rtv->playlist();
962 _editor->latest_regionviews.clear ();
964 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
966 insert_result = modified_playlists.insert (to_playlist);
968 if (insert_result.second) {
969 _editor->session()->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
972 to_playlist->add_region (new_region, where);
973 if (dest_rtv->view()->layer_display() == Stacked) {
974 new_region->set_layer (dest_layer);
975 new_region->set_pending_explicit_relayer (true);
980 if (!_editor->latest_regionviews.empty()) {
981 // XXX why just the first one ? we only expect one
982 // commented out in nick_m's canvas reworking. is that intended?
983 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
984 new_views.push_back (_editor->latest_regionviews.front());
989 motion on the same track. plonk the previously reparented region
990 back to its original canvas group (its streamview).
991 No need to do anything for copies as they are fake regions which will be deleted.
994 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
995 rv->get_canvas_group()->property_y() = 0;
996 rv->get_time_axis_view().reveal_dependent_views (*rv);
998 /* just change the model */
1000 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1002 if (dest_rtv->view()->layer_display() == Stacked) {
1003 rv->region()->set_layer (dest_layer);
1004 rv->region()->set_pending_explicit_relayer (true);
1007 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1009 frozen_insert_result = frozen_playlists.insert(playlist);
1011 if (frozen_insert_result.second) {
1015 XMLNode& before (rv->region()->get_state());
1016 rv->region()->set_position (where, (void*) this);
1017 _editor->session()->add_command (new MementoCommand<Region>(*rv->region(), &before, &playlist->get_state()));
1021 if (changed_tracks && !_copy) {
1023 /* get the playlist where this drag started. we can't use rv->region()->playlist()
1024 because we may have copied the region and it has not been attached to a playlist.
1027 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
1028 ds = source_tv->get_diskstream();
1029 from_playlist = ds->playlist();
1033 assert (from_playlist);
1035 /* moved to a different audio track, without copying */
1037 /* the region that used to be in the old playlist is not
1038 moved to the new one - we use a copy of it. as a result,
1039 any existing editor for the region should no longer be
1043 rv->hide_region_editor();
1044 rv->fake_set_opaque (false);
1046 /* remove the region from the old playlist */
1048 insert_result = modified_playlists.insert (from_playlist);
1050 if (insert_result.second) {
1051 _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
1054 from_playlist->remove_region (rv->region());
1056 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1057 was selected in all of them, then removing it from a playlist will have removed all
1058 trace of it from the selection (i.e. there were N regions selected, we removed 1,
1059 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1060 corresponding regionview, and the selection is now empty).
1062 this could have invalidated any and all iterators into the region selection.
1064 the heuristic we use here is: if the region selection is empty, break out of the loop
1065 here. if the region selection is not empty, then restart the loop because we know that
1066 we must have removed at least the region(view) we've just been working on as well as any
1067 that we processed on previous iterations.
1069 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
1070 we can just iterate.
1073 if (_views.empty()) {
1084 copies.push_back (rv);
1088 if we've created new regions either by copying or moving
1089 to a new track, we want to replace the old selection with the new ones
1091 if (new_views.size() > 0) {
1092 _editor->selection->set (new_views);
1095 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1100 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
1101 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
1104 _editor->commit_reversible_command ();
1106 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1112 RegionMoveDrag::aborted ()
1116 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1123 RegionMotionDrag::aborted ();
1128 RegionMotionDrag::aborted ()
1130 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1131 TimeAxisView* tv = &(*i)->get_time_axis_view ();
1132 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1134 (*i)->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1135 (*i)->get_canvas_group()->property_y() = 0;
1136 (*i)->get_time_axis_view().reveal_dependent_views (**i);
1137 (*i)->fake_set_opaque (false);
1138 (*i)->move (-_total_x_delta, 0);
1139 (*i)->set_height (rtv->view()->child_height ());
1142 _editor->update_canvas_now ();
1147 RegionMotionDrag::x_move_allowed () const
1149 if (Config->get_edit_mode() == Lock) {
1150 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1151 return _x_constrained;
1154 return !_x_constrained;
1158 RegionMotionDrag::copy_regions (GdkEvent* event)
1160 /* duplicate the regionview(s) and region(s) */
1162 list<RegionView*> new_regionviews;
1164 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1166 RegionView* rv = (*i);
1167 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1168 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1170 const boost::shared_ptr<const Region> original = rv->region();
1171 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1175 boost::shared_ptr<AudioRegion> audioregion_copy
1176 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1177 nrv = new AudioRegionView (*arv, audioregion_copy);
1179 boost::shared_ptr<MidiRegion> midiregion_copy
1180 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1181 nrv = new MidiRegionView (*mrv, midiregion_copy);
1186 nrv->get_canvas_group()->show ();
1187 new_regionviews.push_back (nrv);
1189 /* swap _primary to the copy */
1191 if (rv == _primary) {
1195 /* ..and deselect the one we copied */
1197 rv->set_selected (false);
1200 if (new_regionviews.empty()) {
1204 /* reflect the fact that we are dragging the copies */
1206 _views = new_regionviews;
1208 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1211 sync the canvas to what we think is its current state
1212 without it, the canvas seems to
1213 "forget" to update properly after the upcoming reparent()
1214 ..only if the mouse is in rapid motion at the time of the grab.
1215 something to do with regionview creation taking so long?
1217 _editor->update_canvas_now();
1221 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1223 /* Which trackview is this ? */
1225 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1226 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1227 (*layer) = tvp.second;
1229 if (*tv && (*tv)->layer_display() == Overlaid) {
1233 /* The region motion is only processed if the pointer is over
1237 if (!(*tv) || !(*tv)->is_track()) {
1238 /* To make sure we hide the verbose canvas cursor when the mouse is
1239 not held over and audiotrack.
1241 _editor->hide_verbose_canvas_cursor ();
1248 /** @param new_order New track order.
1249 * @param old_order Old track order.
1250 * @param visible_y_low Lowest visible order.
1251 * @return true if y movement should not happen, otherwise false.
1254 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1256 if (new_order != old_order) {
1258 /* this isn't the pointer track */
1262 /* moving up the canvas */
1263 if ( (new_order - y_span) >= tavs.visible_y_low) {
1267 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1268 int32_t visible_tracks = 0;
1269 while (visible_tracks < y_span ) {
1271 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1272 /* passing through a hidden track */
1277 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1278 /* moving to a non-track; disallow */
1284 /* moving beyond the lowest visible track; disallow */
1288 } else if (y_span < 0) {
1290 /* moving down the canvas */
1291 if ((new_order - y_span) <= tavs.visible_y_high) {
1293 int32_t visible_tracks = 0;
1295 while (visible_tracks > y_span ) {
1298 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1299 /* passing through a hidden track */
1304 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1305 /* moving to a non-track; disallow */
1312 /* moving beyond the highest visible track; disallow */
1319 /* this is the pointer's track */
1321 if ((new_order - y_span) > tavs.visible_y_high) {
1322 /* we will overflow */
1324 } else if ((new_order - y_span) < tavs.visible_y_low) {
1325 /* we will overflow */
1334 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1335 : RegionMotionDrag (e, i, p, v, b),
1338 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1340 _dest_trackview = tv;
1341 if (tv->layer_display() == Overlaid) {
1344 _dest_layer = _primary->region()->layer ();
1348 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1349 if (rtv && rtv->is_track()) {
1350 speed = rtv->get_diskstream()->speed ();
1353 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1357 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1359 RegionMotionDrag::start_grab (event, c);
1361 _pointer_frame_offset = grab_frame() - _last_frame_position;
1364 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1365 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1367 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1368 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1370 _primary = v->view()->create_region_view (r, false, false);
1372 _primary->get_canvas_group()->show ();
1373 _primary->set_position (pos, 0);
1374 _views.push_back (_primary);
1376 _last_frame_position = pos;
1378 _item = _primary->get_canvas_group ();
1379 _dest_trackview = v;
1380 _dest_layer = _primary->region()->layer ();
1383 map<RegionView*, pair<RouteTimeAxisView*, int> >
1384 RegionMotionDrag::find_time_axis_views_and_layers ()
1386 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1388 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1390 double ix1, ix2, iy1, iy2;
1391 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1392 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1393 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1395 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1396 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1404 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1406 _editor->update_canvas_now ();
1408 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1410 RouteTimeAxisView* dest_rtv = final[_primary].first;
1412 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1413 _primary->get_canvas_group()->property_y() = 0;
1415 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1417 _editor->begin_reversible_command (_("insert region"));
1418 XMLNode& before = playlist->get_state ();
1419 playlist->add_region (_primary->region (), _last_frame_position);
1420 _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1421 _editor->commit_reversible_command ();
1429 RegionInsertDrag::aborted ()
1434 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1435 : RegionMoveDrag (e, i, p, v, false, false)
1440 struct RegionSelectionByPosition {
1441 bool operator() (RegionView*a, RegionView* b) {
1442 return a->region()->position () < b->region()->position();
1447 RegionSpliceDrag::motion (GdkEvent* event, bool)
1449 RouteTimeAxisView* tv;
1452 if (!check_possible (&tv, &layer)) {
1458 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1464 RegionSelection copy (_editor->selection->regions);
1466 RegionSelectionByPosition cmp;
1469 nframes64_t const pf = adjusted_current_frame (event);
1471 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1473 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1479 boost::shared_ptr<Playlist> playlist;
1481 if ((playlist = atv->playlist()) == 0) {
1485 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1490 if (pf < (*i)->region()->last_frame() + 1) {
1494 if (pf > (*i)->region()->first_frame()) {
1500 playlist->shuffle ((*i)->region(), dir);
1505 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1511 RegionSpliceDrag::aborted ()
1516 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1524 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1526 _dest_trackview = _view;
1528 Drag::start_grab (event);
1533 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1536 // TODO: create region-create-drag region view here
1539 // TODO: resize region-create-drag region view here
1543 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1545 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1551 if (!movement_occurred) {
1552 mtv->add_region (grab_frame ());
1554 motion (event, false);
1555 // TODO: create region-create-drag region here
1560 RegionCreateDrag::aborted ()
1565 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1573 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1576 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1578 Drag::start_grab (event);
1580 region = &cnote->region_view();
1582 double region_start = region->get_position_pixels();
1583 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1585 if (grab_x() <= middle_point) {
1586 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1589 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1593 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1595 if (event->motion.state & Keyboard::PrimaryModifier) {
1601 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1603 if (ms.size() > 1) {
1604 /* has to be relative, may make no sense otherwise */
1608 region->note_selected (cnote, true);
1610 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1611 MidiRegionSelection::iterator next;
1614 (*r)->begin_resizing (at_front);
1620 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1622 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1623 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1624 (*r)->update_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1629 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1631 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1632 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1633 (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1638 NoteResizeDrag::aborted ()
1644 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1650 RegionGainDrag::finished (GdkEvent *, bool)
1656 RegionGainDrag::aborted ()
1661 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1662 : RegionDrag (e, i, p, v)
1663 , _have_transaction (false)
1669 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1672 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1673 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1675 if (tv && tv->is_track()) {
1676 speed = tv->get_diskstream()->speed();
1679 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1680 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1681 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1683 Drag::start_grab (event, _editor->trimmer_cursor);
1685 nframes64_t const pf = adjusted_current_frame (event);
1687 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1688 _operation = ContentsTrim;
1690 /* These will get overridden for a point trim.*/
1691 if (pf < (region_start + region_length/2)) {
1692 /* closer to start */
1693 _operation = StartTrim;
1694 } else if (pf > (region_end - region_length/2)) {
1696 _operation = EndTrim;
1700 switch (_operation) {
1702 _editor->show_verbose_time_cursor (region_start, 10);
1705 _editor->show_verbose_time_cursor (region_end, 10);
1708 _editor->show_verbose_time_cursor (pf, 10);
1714 TrimDrag::motion (GdkEvent* event, bool first_move)
1716 RegionView* rv = _primary;
1717 nframes64_t frame_delta = 0;
1719 bool left_direction;
1720 bool obey_snap = event ? !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) : false;
1722 /* snap modifier works differently here..
1723 its current state has to be passed to the
1724 various trim functions in order to work properly
1728 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1729 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1730 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1732 if (tv && tv->is_track()) {
1733 speed = tv->get_diskstream()->speed();
1736 nframes64_t const pf = adjusted_current_frame (event);
1738 if (last_pointer_frame() > pf) {
1739 left_direction = true;
1741 left_direction = false;
1748 switch (_operation) {
1750 trim_type = "Region start trim";
1753 trim_type = "Region end trim";
1756 trim_type = "Region content trim";
1760 _editor->begin_reversible_command (trim_type);
1761 _have_transaction = true;
1763 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1764 (*i)->fake_set_opaque(false);
1765 (*i)->region()->freeze ();
1767 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1770 arv->temporarily_hide_envelope ();
1773 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1774 insert_result = _editor->motion_frozen_playlists.insert (pl);
1776 if (insert_result.second) {
1777 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1783 if (left_direction) {
1784 frame_delta = (last_pointer_frame() - pf);
1786 frame_delta = (pf - last_pointer_frame());
1789 bool non_overlap_trim = false;
1791 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1792 non_overlap_trim = true;
1795 switch (_operation) {
1797 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1801 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1802 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1808 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1812 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1813 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1820 bool swap_direction = false;
1822 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1823 swap_direction = true;
1826 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1827 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1833 switch (_operation) {
1835 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1838 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1841 _editor->show_verbose_time_cursor (pf, 10);
1848 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1850 if (movement_occurred) {
1851 motion (event, false);
1853 if (!_editor->selection->selected (_primary)) {
1854 _editor->thaw_region_after_trim (*_primary);
1857 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1858 _editor->thaw_region_after_trim (**i);
1859 (*i)->fake_set_opaque (true);
1862 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1864 if (_have_transaction) {
1865 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1869 _editor->motion_frozen_playlists.clear ();
1871 if (_have_transaction) {
1872 _editor->commit_reversible_command();
1876 /* no mouse movement */
1877 _editor->point_trim (event, adjusted_current_frame (event));
1882 TrimDrag::aborted ()
1884 /* Our motion method is changing model state, so use the Undo system
1885 to cancel. Perhaps not ideal, as this will leave an Undo point
1886 behind which may be slightly odd from the user's point of view.
1891 if (_have_transaction) {
1896 pair<nframes64_t, nframes64_t>
1897 TrimDrag::extent () const
1899 /* we only want to autoscroll to keep the most `outward' region edge on-screen,
1900 not the whole region(s) that is/are being trimmed.
1905 switch (_operation) {
1908 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1909 f = min (f, (nframes64_t) (*i)->region()->position());
1913 f = adjusted_current_frame (0);
1917 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1918 f = max (f, (nframes64_t) (*i)->region()->position() + (*i)->region()->length());
1923 return make_pair (f, f);
1927 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1931 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1936 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1939 // create a dummy marker for visual representation of moving the copy.
1940 // The actual copying is not done before we reach the finish callback.
1942 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1943 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1944 *new MeterSection (_marker->meter()));
1946 _item = &new_marker->the_item ();
1947 _marker = new_marker;
1951 MetricSection& section (_marker->meter());
1953 if (!section.movable()) {
1959 Drag::start_grab (event, cursor);
1961 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1963 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1967 MeterMarkerDrag::motion (GdkEvent* event, bool)
1969 nframes64_t const pf = adjusted_current_frame (event);
1971 _marker->set_position (pf);
1973 _editor->show_verbose_time_cursor (pf, 10);
1977 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1979 if (!movement_occurred) {
1983 motion (event, false);
1987 TempoMap& map (_editor->session()->tempo_map());
1988 map.bbt_time (last_pointer_frame(), when);
1990 if (_copy == true) {
1991 _editor->begin_reversible_command (_("copy meter mark"));
1992 XMLNode &before = map.get_state();
1993 map.add_meter (_marker->meter(), when);
1994 XMLNode &after = map.get_state();
1995 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1996 _editor->commit_reversible_command ();
1998 // delete the dummy marker we used for visual representation of copying.
1999 // a new visual marker will show up automatically.
2002 _editor->begin_reversible_command (_("move meter mark"));
2003 XMLNode &before = map.get_state();
2004 map.move_meter (_marker->meter(), when);
2005 XMLNode &after = map.get_state();
2006 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2007 _editor->commit_reversible_command ();
2012 MeterMarkerDrag::aborted ()
2014 _marker->set_position (_marker->meter().frame ());
2017 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2021 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2026 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2031 // create a dummy marker for visual representation of moving the copy.
2032 // The actual copying is not done before we reach the finish callback.
2034 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2035 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2036 *new TempoSection (_marker->tempo()));
2038 _item = &new_marker->the_item ();
2039 _marker = new_marker;
2043 MetricSection& section (_marker->tempo());
2045 if (!section.movable()) {
2050 Drag::start_grab (event, cursor);
2052 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2053 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2057 TempoMarkerDrag::motion (GdkEvent* event, bool)
2059 nframes64_t const pf = adjusted_current_frame (event);
2060 _marker->set_position (pf);
2061 _editor->show_verbose_time_cursor (pf, 10);
2065 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2067 if (!movement_occurred) {
2071 motion (event, false);
2075 TempoMap& map (_editor->session()->tempo_map());
2076 map.bbt_time (last_pointer_frame(), when);
2078 if (_copy == true) {
2079 _editor->begin_reversible_command (_("copy tempo mark"));
2080 XMLNode &before = map.get_state();
2081 map.add_tempo (_marker->tempo(), when);
2082 XMLNode &after = map.get_state();
2083 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2084 _editor->commit_reversible_command ();
2086 // delete the dummy marker we used for visual representation of copying.
2087 // a new visual marker will show up automatically.
2090 _editor->begin_reversible_command (_("move tempo mark"));
2091 XMLNode &before = map.get_state();
2092 map.move_tempo (_marker->tempo(), when);
2093 XMLNode &after = map.get_state();
2094 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2095 _editor->commit_reversible_command ();
2100 TempoMarkerDrag::aborted ()
2102 _marker->set_position (_marker->tempo().frame());
2105 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2109 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2114 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2116 Drag::start_grab (event, c);
2120 nframes64_t where = _editor->event_frame (event, 0, 0);
2122 _editor->snap_to_with_modifier (where, event);
2123 _editor->playhead_cursor->set_position (where);
2127 if (_cursor == _editor->playhead_cursor) {
2128 _editor->_dragging_playhead = true;
2130 if (_editor->session() && _was_rolling && _stop) {
2131 _editor->session()->request_stop ();
2134 if (_editor->session() && _editor->session()->is_auditioning()) {
2135 _editor->session()->cancel_audition ();
2139 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2141 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2145 CursorDrag::motion (GdkEvent* event, bool)
2147 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2149 if (adjusted_frame == last_pointer_frame()) {
2153 _cursor->set_position (adjusted_frame);
2155 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2158 _editor->update_canvas_now ();
2160 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2164 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2166 _editor->_dragging_playhead = false;
2168 if (!movement_occurred && _stop) {
2172 motion (event, false);
2174 if (_item == &_editor->playhead_cursor->canvas_item) {
2175 if (_editor->session()) {
2176 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2177 _editor->_pending_locate_request = true;
2183 CursorDrag::aborted ()
2185 _editor->_dragging_playhead = false;
2186 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2189 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2190 : RegionDrag (e, i, p, v)
2196 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2198 Drag::start_grab (event, cursor);
2200 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2201 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2203 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2204 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2208 FadeInDrag::motion (GdkEvent* event, bool)
2210 nframes64_t fade_length;
2212 nframes64_t const pos = adjusted_current_frame (event);
2214 boost::shared_ptr<Region> region = _primary->region ();
2216 if (pos < (region->position() + 64)) {
2217 fade_length = 64; // this should be a minimum defined somewhere
2218 } else if (pos > region->last_frame()) {
2219 fade_length = region->length();
2221 fade_length = pos - region->position();
2224 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2226 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2232 tmp->reset_fade_in_shape_width (fade_length);
2235 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2239 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2241 if (!movement_occurred) {
2245 nframes64_t fade_length;
2247 nframes64_t const pos = adjusted_current_frame (event);
2249 boost::shared_ptr<Region> region = _primary->region ();
2251 if (pos < (region->position() + 64)) {
2252 fade_length = 64; // this should be a minimum defined somewhere
2253 } else if (pos > region->last_frame()) {
2254 fade_length = region->length();
2256 fade_length = pos - region->position();
2259 _editor->begin_reversible_command (_("change fade in length"));
2261 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2263 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2269 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2270 XMLNode &before = alist->get_state();
2272 tmp->audio_region()->set_fade_in_length (fade_length);
2273 tmp->audio_region()->set_fade_in_active (true);
2275 XMLNode &after = alist->get_state();
2276 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2279 _editor->commit_reversible_command ();
2283 FadeInDrag::aborted ()
2285 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2286 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2292 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2296 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2297 : RegionDrag (e, i, p, v)
2303 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2305 Drag::start_grab (event, cursor);
2307 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2308 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2310 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2311 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2315 FadeOutDrag::motion (GdkEvent* event, bool)
2317 nframes64_t fade_length;
2319 nframes64_t const pos = adjusted_current_frame (event);
2321 boost::shared_ptr<Region> region = _primary->region ();
2323 if (pos > (region->last_frame() - 64)) {
2324 fade_length = 64; // this should really be a minimum fade defined somewhere
2326 else if (pos < region->position()) {
2327 fade_length = region->length();
2330 fade_length = region->last_frame() - pos;
2333 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2335 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2341 tmp->reset_fade_out_shape_width (fade_length);
2344 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2348 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2350 if (!movement_occurred) {
2354 nframes64_t fade_length;
2356 nframes64_t const pos = adjusted_current_frame (event);
2358 boost::shared_ptr<Region> region = _primary->region ();
2360 if (pos > (region->last_frame() - 64)) {
2361 fade_length = 64; // this should really be a minimum fade defined somewhere
2363 else if (pos < region->position()) {
2364 fade_length = region->length();
2367 fade_length = region->last_frame() - pos;
2370 _editor->begin_reversible_command (_("change fade out length"));
2372 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2374 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2380 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2381 XMLNode &before = alist->get_state();
2383 tmp->audio_region()->set_fade_out_length (fade_length);
2384 tmp->audio_region()->set_fade_out_active (true);
2386 XMLNode &after = alist->get_state();
2387 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2390 _editor->commit_reversible_command ();
2394 FadeOutDrag::aborted ()
2396 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2397 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2403 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2407 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2410 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2413 _points.push_back (Gnome::Art::Point (0, 0));
2414 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2416 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2417 _line->property_width_pixels() = 1;
2418 _line->property_points () = _points;
2421 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2424 MarkerDrag::~MarkerDrag ()
2426 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2432 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2434 Drag::start_grab (event, cursor);
2438 Location *location = _editor->find_location_from_marker (_marker, is_start);
2439 _editor->_dragging_edit_point = true;
2441 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2443 update_item (location);
2445 // _drag_line->show();
2446 // _line->raise_to_top();
2449 _editor->show_verbose_time_cursor (location->start(), 10);
2451 _editor->show_verbose_time_cursor (location->end(), 10);
2454 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2457 case Selection::Toggle:
2458 _editor->selection->toggle (_marker);
2460 case Selection::Set:
2461 if (!_editor->selection->selected (_marker)) {
2462 _editor->selection->set (_marker);
2465 case Selection::Extend:
2467 Locations::LocationList ll;
2468 list<Marker*> to_add;
2470 _editor->selection->markers.range (s, e);
2471 s = min (_marker->position(), s);
2472 e = max (_marker->position(), e);
2475 if (e < max_frames) {
2478 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2479 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2480 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2483 to_add.push_back (lm->start);
2486 to_add.push_back (lm->end);
2490 if (!to_add.empty()) {
2491 _editor->selection->add (to_add);
2495 case Selection::Add:
2496 _editor->selection->add (_marker);
2500 /* set up copies for us to manipulate during the drag */
2502 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2503 Location *l = _editor->find_location_from_marker (*i, is_start);
2504 _copied_locations.push_back (new Location (*l));
2509 MarkerDrag::motion (GdkEvent* event, bool)
2511 nframes64_t f_delta = 0;
2513 bool move_both = false;
2515 Location *real_location;
2516 Location *copy_location = 0;
2518 nframes64_t const newframe = adjusted_current_frame (event);
2520 nframes64_t next = newframe;
2522 if (newframe == last_pointer_frame()) {
2526 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2530 MarkerSelection::iterator i;
2531 list<Location*>::iterator x;
2533 /* find the marker we're dragging, and compute the delta */
2535 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2536 x != _copied_locations.end() && i != _editor->selection->markers.end();
2542 if (marker == _marker) {
2544 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2549 if (real_location->is_mark()) {
2550 f_delta = newframe - copy_location->start();
2554 switch (marker->type()) {
2556 case Marker::LoopStart:
2557 case Marker::PunchIn:
2558 f_delta = newframe - copy_location->start();
2562 case Marker::LoopEnd:
2563 case Marker::PunchOut:
2564 f_delta = newframe - copy_location->end();
2567 /* what kind of marker is this ? */
2575 if (i == _editor->selection->markers.end()) {
2576 /* hmm, impossible - we didn't find the dragged marker */
2580 /* now move them all */
2582 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2583 x != _copied_locations.end() && i != _editor->selection->markers.end();
2589 /* call this to find out if its the start or end */
2591 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2595 if (real_location->locked()) {
2599 if (copy_location->is_mark()) {
2603 copy_location->set_start (copy_location->start() + f_delta);
2607 nframes64_t new_start = copy_location->start() + f_delta;
2608 nframes64_t new_end = copy_location->end() + f_delta;
2610 if (is_start) { // start-of-range marker
2613 copy_location->set_start (new_start);
2614 copy_location->set_end (new_end);
2615 } else if (new_start < copy_location->end()) {
2616 copy_location->set_start (new_start);
2618 _editor->snap_to (next, 1, true);
2619 copy_location->set_end (next);
2620 copy_location->set_start (newframe);
2623 } else { // end marker
2626 copy_location->set_end (new_end);
2627 copy_location->set_start (new_start);
2628 } else if (new_end > copy_location->start()) {
2629 copy_location->set_end (new_end);
2630 } else if (newframe > 0) {
2631 _editor->snap_to (next, -1, true);
2632 copy_location->set_start (next);
2633 copy_location->set_end (newframe);
2638 update_item (copy_location);
2640 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2643 lm->set_position (copy_location->start(), copy_location->end());
2647 assert (!_copied_locations.empty());
2649 _editor->show_verbose_time_cursor (newframe, 10);
2652 _editor->update_canvas_now ();
2657 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2659 if (!movement_occurred) {
2661 /* just a click, do nothing but finish
2662 off the selection process
2665 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2668 case Selection::Set:
2669 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2670 _editor->selection->set (_marker);
2674 case Selection::Toggle:
2675 case Selection::Extend:
2676 case Selection::Add:
2683 _editor->_dragging_edit_point = false;
2685 _editor->begin_reversible_command ( _("move marker") );
2686 XMLNode &before = _editor->session()->locations()->get_state();
2688 MarkerSelection::iterator i;
2689 list<Location*>::iterator x;
2692 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2693 x != _copied_locations.end() && i != _editor->selection->markers.end();
2696 Location * location = _editor->find_location_from_marker (*i, is_start);
2700 if (location->locked()) {
2704 if (location->is_mark()) {
2705 location->set_start ((*x)->start());
2707 location->set ((*x)->start(), (*x)->end());
2712 XMLNode &after = _editor->session()->locations()->get_state();
2713 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2714 _editor->commit_reversible_command ();
2720 MarkerDrag::aborted ()
2726 MarkerDrag::update_item (Location* location)
2728 double const x1 = _editor->frame_to_pixel (location->start());
2730 _points.front().set_x(x1);
2731 _points.back().set_x(x1);
2732 _line->property_points() = _points;
2735 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2737 _cumulative_x_drag (0),
2738 _cumulative_y_drag (0)
2740 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2746 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2748 Drag::start_grab (event, _editor->fader_cursor);
2750 // start the grab at the center of the control point so
2751 // the point doesn't 'jump' to the mouse after the first drag
2752 _time_axis_view_grab_x = _point->get_x();
2753 _time_axis_view_grab_y = _point->get_y();
2755 float const fraction = 1 - (_point->get_y() / _point->line().height());
2757 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2759 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2760 event->button.x + 10, event->button.y + 10);
2762 _editor->show_verbose_canvas_cursor ();
2766 ControlPointDrag::motion (GdkEvent* event, bool)
2768 double dx = _drags->current_pointer_x() - last_pointer_x();
2769 double dy = _drags->current_pointer_y() - last_pointer_y();
2771 if (event->button.state & Keyboard::SecondaryModifier) {
2776 /* coordinate in TimeAxisView's space */
2777 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2778 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2780 // calculate zero crossing point. back off by .01 to stay on the
2781 // positive side of zero
2782 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2784 // make sure we hit zero when passing through
2785 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2789 if (_x_constrained) {
2790 cx = _time_axis_view_grab_x;
2792 if (_y_constrained) {
2793 cy = _time_axis_view_grab_y;
2796 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2797 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2801 cy = min ((double) _point->line().height(), cy);
2803 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2805 if (!_x_constrained) {
2806 _editor->snap_to_with_modifier (cx_frames, event);
2809 float const fraction = 1.0 - (cy / _point->line().height());
2811 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2813 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2815 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2819 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2821 if (!movement_occurred) {
2825 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2826 _editor->reset_point_selection ();
2830 motion (event, false);
2832 _point->line().end_drag ();
2836 ControlPointDrag::aborted ()
2838 _point->line().reset ();
2842 ControlPointDrag::active (Editing::MouseMode m)
2844 if (m == Editing::MouseGain) {
2845 /* always active in mouse gain */
2849 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2850 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2853 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2856 _cumulative_y_drag (0)
2861 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2863 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2866 _item = &_line->grab_item ();
2868 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2869 origin, and ditto for y.
2872 double cx = event->button.x;
2873 double cy = event->button.y;
2875 _line->parent_group().w2i (cx, cy);
2877 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2882 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2883 /* no adjacent points */
2887 Drag::start_grab (event, _editor->fader_cursor);
2889 /* store grab start in parent frame */
2891 _time_axis_view_grab_x = cx;
2892 _time_axis_view_grab_y = cy;
2894 double fraction = 1.0 - (cy / _line->height());
2896 _line->start_drag_line (before, after, fraction);
2898 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2899 event->button.x + 10, event->button.y + 10);
2901 _editor->show_verbose_canvas_cursor ();
2905 LineDrag::motion (GdkEvent* event, bool)
2907 double dy = _drags->current_pointer_y() - last_pointer_y();
2909 if (event->button.state & Keyboard::SecondaryModifier) {
2913 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2915 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2918 cy = min ((double) _line->height(), cy);
2920 double const fraction = 1.0 - (cy / _line->height());
2924 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2930 /* we are ignoring x position for this drag, so we can just pass in anything */
2931 _line->drag_motion (0, fraction, true, push);
2933 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2937 LineDrag::finished (GdkEvent* event, bool)
2939 motion (event, false);
2944 LineDrag::aborted ()
2950 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2952 Drag::start_grab (event);
2953 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2957 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2964 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2966 nframes64_t grab = grab_frame ();
2967 if (Config->get_rubberbanding_snaps_to_grid ()) {
2968 _editor->snap_to_with_modifier (grab, event);
2971 /* base start and end on initial click position */
2981 if (_drags->current_pointer_y() < grab_y()) {
2982 y1 = _drags->current_pointer_y();
2985 y2 = _drags->current_pointer_y();
2990 if (start != end || y1 != y2) {
2992 double x1 = _editor->frame_to_pixel (start);
2993 double x2 = _editor->frame_to_pixel (end);
2995 _editor->rubberband_rect->property_x1() = x1;
2996 _editor->rubberband_rect->property_y1() = y1;
2997 _editor->rubberband_rect->property_x2() = x2;
2998 _editor->rubberband_rect->property_y2() = y2;
3000 _editor->rubberband_rect->show();
3001 _editor->rubberband_rect->raise_to_top();
3003 _editor->show_verbose_time_cursor (pf, 10);
3008 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3010 if (movement_occurred) {
3012 motion (event, false);
3015 if (_drags->current_pointer_y() < grab_y()) {
3016 y1 = _drags->current_pointer_y();
3019 y2 = _drags->current_pointer_y();
3024 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3027 _editor->begin_reversible_command (_("rubberband selection"));
3029 if (grab_frame() < last_pointer_frame()) {
3030 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3032 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3036 _editor->commit_reversible_command ();
3040 if (!getenv("ARDOUR_SAE")) {
3041 _editor->selection->clear_tracks();
3043 _editor->selection->clear_regions();
3044 _editor->selection->clear_points ();
3045 _editor->selection->clear_lines ();
3048 _editor->rubberband_rect->hide();
3052 RubberbandSelectDrag::aborted ()
3054 _editor->rubberband_rect->hide ();
3058 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3060 Drag::start_grab (event);
3062 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3066 TimeFXDrag::motion (GdkEvent* event, bool)
3068 RegionView* rv = _primary;
3070 nframes64_t const pf = adjusted_current_frame (event);
3072 if (pf > rv->region()->position()) {
3073 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3076 _editor->show_verbose_time_cursor (pf, 10);
3080 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3082 _primary->get_time_axis_view().hide_timestretch ();
3084 if (!movement_occurred) {
3088 if (last_pointer_frame() < _primary->region()->position()) {
3089 /* backwards drag of the left edge - not usable */
3093 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3095 float percentage = (double) newlen / (double) _primary->region()->length();
3097 #ifndef USE_RUBBERBAND
3098 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3099 if (_primary->region()->data_type() == DataType::AUDIO) {
3100 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3104 _editor->begin_reversible_command (_("timestretch"));
3106 // XXX how do timeFX on multiple regions ?
3111 if (!_editor->time_stretch (rs, percentage) == 0) {
3112 error << _("An error occurred while executing time stretch operation") << endmsg;
3117 TimeFXDrag::aborted ()
3119 _primary->get_time_axis_view().hide_timestretch ();
3124 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3126 Drag::start_grab (event);
3130 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3132 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3136 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3138 if (movement_occurred && _editor->session()) {
3139 /* make sure we stop */
3140 _editor->session()->request_transport_speed (0.0);
3145 ScrubDrag::aborted ()
3150 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3154 , _original_pointer_time_axis (-1)
3155 , _last_pointer_time_axis (-1)
3161 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3163 nframes64_t start = 0;
3164 nframes64_t end = 0;
3166 if (_editor->session() == 0) {
3170 Gdk::Cursor* cursor = 0;
3172 switch (_operation) {
3173 case CreateSelection:
3174 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3179 cursor = _editor->selector_cursor;
3180 Drag::start_grab (event, cursor);
3183 case SelectionStartTrim:
3184 if (_editor->clicked_axisview) {
3185 _editor->clicked_axisview->order_selection_trims (_item, true);
3187 Drag::start_grab (event, _editor->trimmer_cursor);
3188 start = _editor->selection->time[_editor->clicked_selection].start;
3189 _pointer_frame_offset = grab_frame() - start;
3192 case SelectionEndTrim:
3193 if (_editor->clicked_axisview) {
3194 _editor->clicked_axisview->order_selection_trims (_item, false);
3196 Drag::start_grab (event, _editor->trimmer_cursor);
3197 end = _editor->selection->time[_editor->clicked_selection].end;
3198 _pointer_frame_offset = grab_frame() - end;
3202 start = _editor->selection->time[_editor->clicked_selection].start;
3203 Drag::start_grab (event, cursor);
3204 _pointer_frame_offset = grab_frame() - start;
3208 if (_operation == SelectionMove) {
3209 _editor->show_verbose_time_cursor (start, 10);
3211 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3214 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3218 SelectionDrag::motion (GdkEvent* event, bool first_move)
3220 nframes64_t start = 0;
3221 nframes64_t end = 0;
3224 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3225 if (pending_time_axis.first == 0) {
3229 nframes64_t const pending_position = adjusted_current_frame (event);
3231 /* only alter selection if things have changed */
3233 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3237 switch (_operation) {
3238 case CreateSelection:
3240 nframes64_t grab = grab_frame ();
3243 _editor->snap_to (grab);
3246 if (pending_position < grab_frame()) {
3247 start = pending_position;
3250 end = pending_position;
3254 /* first drag: Either add to the selection
3255 or create a new selection
3261 /* adding to the selection */
3262 _editor->selection->add (_editor->clicked_axisview);
3263 _editor->clicked_selection = _editor->selection->add (start, end);
3268 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3269 _editor->selection->set (_editor->clicked_axisview);
3272 _editor->clicked_selection = _editor->selection->set (start, end);
3276 /* select the track that we're in */
3277 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3278 _editor->selection->add (pending_time_axis.first);
3279 _added_time_axes.push_back (pending_time_axis.first);
3282 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3283 tracks that we selected in the first place.
3286 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3287 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3289 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3290 while (i != _added_time_axes.end()) {
3292 list<TimeAxisView*>::iterator tmp = i;
3295 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3296 _editor->selection->remove (*i);
3297 _added_time_axes.remove (*i);
3306 case SelectionStartTrim:
3308 start = _editor->selection->time[_editor->clicked_selection].start;
3309 end = _editor->selection->time[_editor->clicked_selection].end;
3311 if (pending_position > end) {
3314 start = pending_position;
3318 case SelectionEndTrim:
3320 start = _editor->selection->time[_editor->clicked_selection].start;
3321 end = _editor->selection->time[_editor->clicked_selection].end;
3323 if (pending_position < start) {
3326 end = pending_position;
3333 start = _editor->selection->time[_editor->clicked_selection].start;
3334 end = _editor->selection->time[_editor->clicked_selection].end;
3336 length = end - start;
3338 start = pending_position;
3339 _editor->snap_to (start);
3341 end = start + length;
3346 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3347 _editor->start_canvas_autoscroll (1, 0);
3351 _editor->selection->replace (_editor->clicked_selection, start, end);
3354 if (_operation == SelectionMove) {
3355 _editor->show_verbose_time_cursor(start, 10);
3357 _editor->show_verbose_time_cursor(pending_position, 10);
3362 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3364 Session* s = _editor->session();
3366 if (movement_occurred) {
3367 motion (event, false);
3368 /* XXX this is not object-oriented programming at all. ick */
3369 if (_editor->selection->time.consolidate()) {
3370 _editor->selection->TimeChanged ();
3373 /* XXX what if its a music time selection? */
3374 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3375 s->request_play_range (&_editor->selection->time, true);
3380 /* just a click, no pointer movement.*/
3382 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3383 _editor->selection->clear_time();
3386 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3387 _editor->selection->set (_editor->clicked_axisview);
3390 if (s && s->get_play_range () && s->transport_rolling()) {
3391 s->request_stop (false, false);
3396 _editor->stop_canvas_autoscroll ();
3400 SelectionDrag::aborted ()
3405 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3410 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3411 _drag_rect->hide ();
3413 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3414 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3418 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3420 if (_editor->session() == 0) {
3424 Gdk::Cursor* cursor = 0;
3426 if (!_editor->temp_location) {
3427 _editor->temp_location = new Location;
3430 switch (_operation) {
3431 case CreateRangeMarker:
3432 case CreateTransportMarker:
3433 case CreateCDMarker:
3435 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3440 cursor = _editor->selector_cursor;
3444 Drag::start_grab (event, cursor);
3446 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3450 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3452 nframes64_t start = 0;
3453 nframes64_t end = 0;
3454 ArdourCanvas::SimpleRect *crect;
3456 switch (_operation) {
3457 case CreateRangeMarker:
3458 crect = _editor->range_bar_drag_rect;
3460 case CreateTransportMarker:
3461 crect = _editor->transport_bar_drag_rect;
3463 case CreateCDMarker:
3464 crect = _editor->cd_marker_bar_drag_rect;
3467 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3472 nframes64_t const pf = adjusted_current_frame (event);
3474 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3475 nframes64_t grab = grab_frame ();
3476 _editor->snap_to (grab);
3478 if (pf < grab_frame()) {
3486 /* first drag: Either add to the selection
3487 or create a new selection.
3492 _editor->temp_location->set (start, end);
3496 update_item (_editor->temp_location);
3498 //_drag_rect->raise_to_top();
3503 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3504 _editor->start_canvas_autoscroll (1, 0);
3508 _editor->temp_location->set (start, end);
3510 double x1 = _editor->frame_to_pixel (start);
3511 double x2 = _editor->frame_to_pixel (end);
3512 crect->property_x1() = x1;
3513 crect->property_x2() = x2;
3515 update_item (_editor->temp_location);
3518 _editor->show_verbose_time_cursor (pf, 10);
3523 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3525 Location * newloc = 0;
3529 if (movement_occurred) {
3530 motion (event, false);
3533 switch (_operation) {
3534 case CreateRangeMarker:
3535 case CreateCDMarker:
3537 _editor->begin_reversible_command (_("new range marker"));
3538 XMLNode &before = _editor->session()->locations()->get_state();
3539 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3540 if (_operation == CreateCDMarker) {
3541 flags = Location::IsRangeMarker | Location::IsCDMarker;
3542 _editor->cd_marker_bar_drag_rect->hide();
3545 flags = Location::IsRangeMarker;
3546 _editor->range_bar_drag_rect->hide();
3548 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3549 _editor->session()->locations()->add (newloc, true);
3550 XMLNode &after = _editor->session()->locations()->get_state();
3551 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3552 _editor->commit_reversible_command ();
3556 case CreateTransportMarker:
3557 // popup menu to pick loop or punch
3558 _editor->new_transport_marker_context_menu (&event->button, _item);
3562 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3564 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3569 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3571 if (end == max_frames) {
3572 end = _editor->session()->current_end_frame ();
3575 if (start == max_frames) {
3576 start = _editor->session()->current_start_frame ();
3579 switch (_editor->mouse_mode) {
3581 /* find the two markers on either side and then make the selection from it */
3582 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3586 /* find the two markers on either side of the click and make the range out of it */
3587 _editor->selection->set (start, end);
3596 _editor->stop_canvas_autoscroll ();
3600 RangeMarkerBarDrag::aborted ()
3606 RangeMarkerBarDrag::update_item (Location* location)
3608 double const x1 = _editor->frame_to_pixel (location->start());
3609 double const x2 = _editor->frame_to_pixel (location->end());
3611 _drag_rect->property_x1() = x1;
3612 _drag_rect->property_x2() = x2;
3616 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3618 Drag::start_grab (event, _editor->zoom_cursor);
3619 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3623 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3628 nframes64_t const pf = adjusted_current_frame (event);
3630 nframes64_t grab = grab_frame ();
3631 _editor->snap_to_with_modifier (grab, event);
3633 /* base start and end on initial click position */
3645 _editor->zoom_rect->show();
3646 _editor->zoom_rect->raise_to_top();
3649 _editor->reposition_zoom_rect(start, end);
3651 _editor->show_verbose_time_cursor (pf, 10);
3656 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3658 if (movement_occurred) {
3659 motion (event, false);
3661 if (grab_frame() < last_pointer_frame()) {
3662 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3664 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3667 _editor->temporal_zoom_to_frame (false, grab_frame());
3669 temporal_zoom_step (false);
3670 center_screen (grab_frame());
3674 _editor->zoom_rect->hide();
3678 MouseZoomDrag::aborted ()
3680 _editor->zoom_rect->hide ();
3683 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3686 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3687 region = &cnote->region_view();
3691 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3693 Drag::start_grab (event);
3696 drag_delta_note = 0;
3701 event_x = _drags->current_pointer_x();
3702 event_y = _drags->current_pointer_y();
3704 _item->property_parent().get_value()->w2i(event_x, event_y);
3706 last_x = region->snap_to_pixel(event_x);
3709 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3711 if (!(was_selected = cnote->selected())) {
3713 /* tertiary-click means extend selection - we'll do that on button release,
3714 so don't add it here, because otherwise we make it hard to figure
3715 out the "extend-to" range.
3718 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3721 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3724 region->note_selected (cnote, true);
3726 region->unique_select (cnote);
3733 NoteDrag::motion (GdkEvent*, bool)
3735 MidiStreamView* streamview = region->midi_stream_view();
3739 event_x = _drags->current_pointer_x();
3740 event_y = _drags->current_pointer_y();
3742 _item->property_parent().get_value()->w2i(event_x, event_y);
3744 event_x = region->snap_to_pixel(event_x);
3746 double dx = event_x - last_x;
3747 double dy = event_y - last_y;
3752 // Snap to note rows
3754 if (abs (dy) < streamview->note_height()) {
3757 int8_t this_delta_note;
3759 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3761 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3763 drag_delta_note -= this_delta_note;
3764 dy = streamview->note_height() * this_delta_note;
3765 last_y = last_y + dy;
3769 region->move_selection (dx, dy);
3771 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3773 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3774 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3775 _editor->show_verbose_canvas_cursor_with (buf);
3780 NoteDrag::finished (GdkEvent* ev, bool moved)
3782 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3785 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3788 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3790 region->note_deselected (cnote);
3793 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3794 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3796 if (!extend && !add && region->selection_size() > 1) {
3797 region->unique_select(cnote);
3798 } else if (extend) {
3799 region->note_selected (cnote, true, true);
3801 /* it was added during button press */
3806 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3811 NoteDrag::aborted ()
3816 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3819 , _nothing_to_drag (false)
3821 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3824 _line = _atav->line ();
3828 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3830 Drag::start_grab (event, cursor);
3832 list<ControlPoint*> points;
3834 XMLNode* state = &_line->get_state ();
3836 if (_ranges.empty()) {
3838 uint32_t const N = _line->npoints ();
3839 for (uint32_t i = 0; i < N; ++i) {
3840 points.push_back (_line->nth (i));
3845 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3846 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3848 /* fade into and out of the region that we're dragging;
3849 64 samples length plucked out of thin air.
3851 nframes64_t const h = (j->start + j->end) / 2;
3852 nframes64_t a = j->start + 64;
3856 nframes64_t b = j->end - 64;
3861 the_list->add (j->start, the_list->eval (j->start));
3862 _line->add_always_in_view (j->start);
3863 the_list->add (a, the_list->eval (a));
3864 _line->add_always_in_view (a);
3865 the_list->add (b, the_list->eval (b));
3866 _line->add_always_in_view (b);
3867 the_list->add (j->end, the_list->eval (j->end));
3868 _line->add_always_in_view (j->end);
3871 uint32_t const N = _line->npoints ();
3872 for (uint32_t i = 0; i < N; ++i) {
3874 ControlPoint* p = _line->nth (i);
3876 list<AudioRange>::const_iterator j = _ranges.begin ();
3877 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3881 if (j != _ranges.end()) {
3882 points.push_back (p);
3887 if (points.empty()) {
3888 _nothing_to_drag = true;
3892 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3896 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3898 if (_nothing_to_drag) {
3902 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3904 /* we are ignoring x position for this drag, so we can just pass in anything */
3905 _line->drag_motion (0, f, true, false);
3909 AutomationRangeDrag::finished (GdkEvent* event, bool)
3911 if (_nothing_to_drag) {
3915 motion (event, false);
3917 _line->clear_always_in_view ();
3921 AutomationRangeDrag::aborted ()
3923 _line->clear_always_in_view ();