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 insert_result = modified_playlists.insert (playlist);
1009 if (insert_result.second) {
1010 _editor->session()->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
1012 /* freeze to avoid lots of relayering in the case of a multi-region drag */
1013 frozen_insert_result = frozen_playlists.insert(playlist);
1015 if (frozen_insert_result.second) {
1019 rv->region()->set_position (where, (void*) this);
1022 if (changed_tracks && !_copy) {
1024 /* get the playlist where this drag started. we can't use rv->region()->playlist()
1025 because we may have copied the region and it has not been attached to a playlist.
1028 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
1029 ds = source_tv->get_diskstream();
1030 from_playlist = ds->playlist();
1034 assert (from_playlist);
1036 /* moved to a different audio track, without copying */
1038 /* the region that used to be in the old playlist is not
1039 moved to the new one - we use a copy of it. as a result,
1040 any existing editor for the region should no longer be
1044 rv->hide_region_editor();
1045 rv->fake_set_opaque (false);
1047 /* remove the region from the old playlist */
1049 insert_result = modified_playlists.insert (from_playlist);
1051 if (insert_result.second) {
1052 _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
1055 from_playlist->remove_region (rv->region());
1057 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1058 was selected in all of them, then removing it from a playlist will have removed all
1059 trace of it from the selection (i.e. there were N regions selected, we removed 1,
1060 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1061 corresponding regionview, and the selection is now empty).
1063 this could have invalidated any and all iterators into the region selection.
1065 the heuristic we use here is: if the region selection is empty, break out of the loop
1066 here. if the region selection is not empty, then restart the loop because we know that
1067 we must have removed at least the region(view) we've just been working on as well as any
1068 that we processed on previous iterations.
1070 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
1071 we can just iterate.
1074 if (_views.empty()) {
1085 copies.push_back (rv);
1089 if we've created new regions either by copying or moving
1090 to a new track, we want to replace the old selection with the new ones
1092 if (new_views.size() > 0) {
1093 _editor->selection->set (new_views);
1096 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1101 for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
1102 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
1105 _editor->commit_reversible_command ();
1107 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1113 RegionMoveDrag::aborted ()
1117 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1124 RegionMotionDrag::aborted ();
1129 RegionMotionDrag::aborted ()
1131 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1132 TimeAxisView* tv = &(*i)->get_time_axis_view ();
1133 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1135 (*i)->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1136 (*i)->get_canvas_group()->property_y() = 0;
1137 (*i)->get_time_axis_view().reveal_dependent_views (**i);
1138 (*i)->fake_set_opaque (false);
1139 (*i)->move (-_total_x_delta, 0);
1140 (*i)->set_height (rtv->view()->child_height ());
1143 _editor->update_canvas_now ();
1148 RegionMotionDrag::x_move_allowed () const
1150 if (Config->get_edit_mode() == Lock) {
1151 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1152 return _x_constrained;
1155 return !_x_constrained;
1159 RegionMotionDrag::copy_regions (GdkEvent* event)
1161 /* duplicate the regionview(s) and region(s) */
1163 list<RegionView*> new_regionviews;
1165 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1167 RegionView* rv = (*i);
1168 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1169 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1171 const boost::shared_ptr<const Region> original = rv->region();
1172 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1176 boost::shared_ptr<AudioRegion> audioregion_copy
1177 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1178 nrv = new AudioRegionView (*arv, audioregion_copy);
1180 boost::shared_ptr<MidiRegion> midiregion_copy
1181 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1182 nrv = new MidiRegionView (*mrv, midiregion_copy);
1187 nrv->get_canvas_group()->show ();
1188 new_regionviews.push_back (nrv);
1190 /* swap _primary to the copy */
1192 if (rv == _primary) {
1196 /* ..and deselect the one we copied */
1198 rv->set_selected (false);
1201 if (new_regionviews.empty()) {
1205 /* reflect the fact that we are dragging the copies */
1207 _views = new_regionviews;
1209 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1212 sync the canvas to what we think is its current state
1213 without it, the canvas seems to
1214 "forget" to update properly after the upcoming reparent()
1215 ..only if the mouse is in rapid motion at the time of the grab.
1216 something to do with regionview creation taking so long?
1218 _editor->update_canvas_now();
1222 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1224 /* Which trackview is this ? */
1226 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1227 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1228 (*layer) = tvp.second;
1230 if (*tv && (*tv)->layer_display() == Overlaid) {
1234 /* The region motion is only processed if the pointer is over
1238 if (!(*tv) || !(*tv)->is_track()) {
1239 /* To make sure we hide the verbose canvas cursor when the mouse is
1240 not held over and audiotrack.
1242 _editor->hide_verbose_canvas_cursor ();
1249 /** @param new_order New track order.
1250 * @param old_order Old track order.
1251 * @param visible_y_low Lowest visible order.
1252 * @return true if y movement should not happen, otherwise false.
1255 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1257 if (new_order != old_order) {
1259 /* this isn't the pointer track */
1263 /* moving up the canvas */
1264 if ( (new_order - y_span) >= tavs.visible_y_low) {
1268 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1269 int32_t visible_tracks = 0;
1270 while (visible_tracks < y_span ) {
1272 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1273 /* passing through a hidden track */
1278 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1279 /* moving to a non-track; disallow */
1285 /* moving beyond the lowest visible track; disallow */
1289 } else if (y_span < 0) {
1291 /* moving down the canvas */
1292 if ((new_order - y_span) <= tavs.visible_y_high) {
1294 int32_t visible_tracks = 0;
1296 while (visible_tracks > y_span ) {
1299 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1300 /* passing through a hidden track */
1305 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1306 /* moving to a non-track; disallow */
1313 /* moving beyond the highest visible track; disallow */
1320 /* this is the pointer's track */
1322 if ((new_order - y_span) > tavs.visible_y_high) {
1323 /* we will overflow */
1325 } else if ((new_order - y_span) < tavs.visible_y_low) {
1326 /* we will overflow */
1335 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1336 : RegionMotionDrag (e, i, p, v, b),
1339 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1341 _dest_trackview = tv;
1342 if (tv->layer_display() == Overlaid) {
1345 _dest_layer = _primary->region()->layer ();
1349 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1350 if (rtv && rtv->is_track()) {
1351 speed = rtv->get_diskstream()->speed ();
1354 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1358 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1360 RegionMotionDrag::start_grab (event, c);
1362 _pointer_frame_offset = grab_frame() - _last_frame_position;
1365 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1366 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1368 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1369 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1371 _primary = v->view()->create_region_view (r, false, false);
1373 _primary->get_canvas_group()->show ();
1374 _primary->set_position (pos, 0);
1375 _views.push_back (_primary);
1377 _last_frame_position = pos;
1379 _item = _primary->get_canvas_group ();
1380 _dest_trackview = v;
1381 _dest_layer = _primary->region()->layer ();
1384 map<RegionView*, pair<RouteTimeAxisView*, int> >
1385 RegionMotionDrag::find_time_axis_views_and_layers ()
1387 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1389 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1391 double ix1, ix2, iy1, iy2;
1392 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1393 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1394 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1396 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1397 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1405 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1407 _editor->update_canvas_now ();
1409 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1411 RouteTimeAxisView* dest_rtv = final[_primary].first;
1413 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1414 _primary->get_canvas_group()->property_y() = 0;
1416 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1418 _editor->begin_reversible_command (_("insert region"));
1419 XMLNode& before = playlist->get_state ();
1420 playlist->add_region (_primary->region (), _last_frame_position);
1421 _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1422 _editor->commit_reversible_command ();
1430 RegionInsertDrag::aborted ()
1435 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1436 : RegionMoveDrag (e, i, p, v, false, false)
1441 struct RegionSelectionByPosition {
1442 bool operator() (RegionView*a, RegionView* b) {
1443 return a->region()->position () < b->region()->position();
1448 RegionSpliceDrag::motion (GdkEvent* event, bool)
1450 RouteTimeAxisView* tv;
1453 if (!check_possible (&tv, &layer)) {
1459 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1465 RegionSelection copy (_editor->selection->regions);
1467 RegionSelectionByPosition cmp;
1470 nframes64_t const pf = adjusted_current_frame (event);
1472 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1474 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1480 boost::shared_ptr<Playlist> playlist;
1482 if ((playlist = atv->playlist()) == 0) {
1486 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1491 if (pf < (*i)->region()->last_frame() + 1) {
1495 if (pf > (*i)->region()->first_frame()) {
1501 playlist->shuffle ((*i)->region(), dir);
1506 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1512 RegionSpliceDrag::aborted ()
1517 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1525 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1527 _dest_trackview = _view;
1529 Drag::start_grab (event);
1534 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1537 // TODO: create region-create-drag region view here
1540 // TODO: resize region-create-drag region view here
1544 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1546 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1552 if (!movement_occurred) {
1553 mtv->add_region (grab_frame ());
1555 motion (event, false);
1556 // TODO: create region-create-drag region here
1561 RegionCreateDrag::aborted ()
1566 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1574 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1577 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1579 Drag::start_grab (event);
1581 region = &cnote->region_view();
1583 double region_start = region->get_position_pixels();
1584 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1586 if (grab_x() <= middle_point) {
1587 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1590 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1594 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1596 if (event->motion.state & Keyboard::PrimaryModifier) {
1602 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1604 if (ms.size() > 1) {
1605 /* has to be relative, may make no sense otherwise */
1609 region->note_selected (cnote, true);
1611 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1612 MidiRegionSelection::iterator next;
1615 (*r)->begin_resizing (at_front);
1621 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1623 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1624 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1625 (*r)->update_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1630 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1632 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1633 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1634 (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1639 NoteResizeDrag::aborted ()
1645 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1651 RegionGainDrag::finished (GdkEvent *, bool)
1657 RegionGainDrag::aborted ()
1662 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1663 : RegionDrag (e, i, p, v)
1664 , _have_transaction (false)
1670 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1673 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1674 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1676 if (tv && tv->is_track()) {
1677 speed = tv->get_diskstream()->speed();
1680 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1681 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1682 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1684 Drag::start_grab (event, _editor->trimmer_cursor);
1686 nframes64_t const pf = adjusted_current_frame (event);
1688 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1689 _operation = ContentsTrim;
1691 /* These will get overridden for a point trim.*/
1692 if (pf < (region_start + region_length/2)) {
1693 /* closer to start */
1694 _operation = StartTrim;
1695 } else if (pf > (region_end - region_length/2)) {
1697 _operation = EndTrim;
1701 switch (_operation) {
1703 _editor->show_verbose_time_cursor (region_start, 10);
1706 _editor->show_verbose_time_cursor (region_end, 10);
1709 _editor->show_verbose_time_cursor (pf, 10);
1715 TrimDrag::motion (GdkEvent* event, bool first_move)
1717 RegionView* rv = _primary;
1718 nframes64_t frame_delta = 0;
1720 bool left_direction;
1721 bool obey_snap = event ? !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) : false;
1723 /* snap modifier works differently here..
1724 its current state has to be passed to the
1725 various trim functions in order to work properly
1729 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1730 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1731 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1733 if (tv && tv->is_track()) {
1734 speed = tv->get_diskstream()->speed();
1737 nframes64_t const pf = adjusted_current_frame (event);
1739 if (last_pointer_frame() > pf) {
1740 left_direction = true;
1742 left_direction = false;
1749 switch (_operation) {
1751 trim_type = "Region start trim";
1754 trim_type = "Region end trim";
1757 trim_type = "Region content trim";
1761 _editor->begin_reversible_command (trim_type);
1762 _have_transaction = true;
1764 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1765 (*i)->fake_set_opaque(false);
1766 (*i)->region()->freeze ();
1768 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1771 arv->temporarily_hide_envelope ();
1774 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1775 insert_result = _editor->motion_frozen_playlists.insert (pl);
1777 if (insert_result.second) {
1778 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1784 if (left_direction) {
1785 frame_delta = (last_pointer_frame() - pf);
1787 frame_delta = (pf - last_pointer_frame());
1790 bool non_overlap_trim = false;
1792 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1793 non_overlap_trim = true;
1796 switch (_operation) {
1798 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1802 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1803 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1809 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1813 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1814 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1821 bool swap_direction = false;
1823 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1824 swap_direction = true;
1827 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1828 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1834 switch (_operation) {
1836 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1839 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1842 _editor->show_verbose_time_cursor (pf, 10);
1849 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1851 if (movement_occurred) {
1852 motion (event, false);
1854 if (!_editor->selection->selected (_primary)) {
1855 _editor->thaw_region_after_trim (*_primary);
1858 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1859 _editor->thaw_region_after_trim (**i);
1860 (*i)->fake_set_opaque (true);
1863 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1865 if (_have_transaction) {
1866 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1870 _editor->motion_frozen_playlists.clear ();
1872 if (_have_transaction) {
1873 _editor->commit_reversible_command();
1877 /* no mouse movement */
1878 _editor->point_trim (event, adjusted_current_frame (event));
1883 TrimDrag::aborted ()
1885 /* Our motion method is changing model state, so use the Undo system
1886 to cancel. Perhaps not ideal, as this will leave an Undo point
1887 behind which may be slightly odd from the user's point of view.
1892 if (_have_transaction) {
1897 pair<nframes64_t, nframes64_t>
1898 TrimDrag::extent () const
1900 /* we only want to autoscroll to keep the most `outward' region edge on-screen,
1901 not the whole region(s) that is/are being trimmed.
1906 switch (_operation) {
1909 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1910 f = min (f, (nframes64_t) (*i)->region()->position());
1914 f = adjusted_current_frame (0);
1918 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1919 f = max (f, (nframes64_t) (*i)->region()->position() + (*i)->region()->length());
1924 return make_pair (f, f);
1928 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1932 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1937 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1940 // create a dummy marker for visual representation of moving the copy.
1941 // The actual copying is not done before we reach the finish callback.
1943 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1944 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1945 *new MeterSection (_marker->meter()));
1947 _item = &new_marker->the_item ();
1948 _marker = new_marker;
1952 MetricSection& section (_marker->meter());
1954 if (!section.movable()) {
1960 Drag::start_grab (event, cursor);
1962 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1964 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1968 MeterMarkerDrag::motion (GdkEvent* event, bool)
1970 nframes64_t const pf = adjusted_current_frame (event);
1972 _marker->set_position (pf);
1974 _editor->show_verbose_time_cursor (pf, 10);
1978 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1980 if (!movement_occurred) {
1984 motion (event, false);
1988 TempoMap& map (_editor->session()->tempo_map());
1989 map.bbt_time (last_pointer_frame(), when);
1991 if (_copy == true) {
1992 _editor->begin_reversible_command (_("copy meter mark"));
1993 XMLNode &before = map.get_state();
1994 map.add_meter (_marker->meter(), when);
1995 XMLNode &after = map.get_state();
1996 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1997 _editor->commit_reversible_command ();
1999 // delete the dummy marker we used for visual representation of copying.
2000 // a new visual marker will show up automatically.
2003 _editor->begin_reversible_command (_("move meter mark"));
2004 XMLNode &before = map.get_state();
2005 map.move_meter (_marker->meter(), when);
2006 XMLNode &after = map.get_state();
2007 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2008 _editor->commit_reversible_command ();
2013 MeterMarkerDrag::aborted ()
2015 _marker->set_position (_marker->meter().frame ());
2018 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2022 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2027 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2032 // create a dummy marker for visual representation of moving the copy.
2033 // The actual copying is not done before we reach the finish callback.
2035 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2036 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2037 *new TempoSection (_marker->tempo()));
2039 _item = &new_marker->the_item ();
2040 _marker = new_marker;
2044 MetricSection& section (_marker->tempo());
2046 if (!section.movable()) {
2051 Drag::start_grab (event, cursor);
2053 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2054 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2058 TempoMarkerDrag::motion (GdkEvent* event, bool)
2060 nframes64_t const pf = adjusted_current_frame (event);
2061 _marker->set_position (pf);
2062 _editor->show_verbose_time_cursor (pf, 10);
2066 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2068 if (!movement_occurred) {
2072 motion (event, false);
2076 TempoMap& map (_editor->session()->tempo_map());
2077 map.bbt_time (last_pointer_frame(), when);
2079 if (_copy == true) {
2080 _editor->begin_reversible_command (_("copy tempo mark"));
2081 XMLNode &before = map.get_state();
2082 map.add_tempo (_marker->tempo(), when);
2083 XMLNode &after = map.get_state();
2084 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2085 _editor->commit_reversible_command ();
2087 // delete the dummy marker we used for visual representation of copying.
2088 // a new visual marker will show up automatically.
2091 _editor->begin_reversible_command (_("move tempo mark"));
2092 XMLNode &before = map.get_state();
2093 map.move_tempo (_marker->tempo(), when);
2094 XMLNode &after = map.get_state();
2095 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2096 _editor->commit_reversible_command ();
2101 TempoMarkerDrag::aborted ()
2103 _marker->set_position (_marker->tempo().frame());
2106 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2110 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2115 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2117 Drag::start_grab (event, c);
2121 nframes64_t where = _editor->event_frame (event, 0, 0);
2123 _editor->snap_to_with_modifier (where, event);
2124 _editor->playhead_cursor->set_position (where);
2128 if (_cursor == _editor->playhead_cursor) {
2129 _editor->_dragging_playhead = true;
2131 if (_editor->session() && _was_rolling && _stop) {
2132 _editor->session()->request_stop ();
2135 if (_editor->session() && _editor->session()->is_auditioning()) {
2136 _editor->session()->cancel_audition ();
2140 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2142 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2146 CursorDrag::motion (GdkEvent* event, bool)
2148 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2150 if (adjusted_frame == last_pointer_frame()) {
2154 _cursor->set_position (adjusted_frame);
2156 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2159 _editor->update_canvas_now ();
2161 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2165 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2167 _editor->_dragging_playhead = false;
2169 if (!movement_occurred && _stop) {
2173 motion (event, false);
2175 if (_item == &_editor->playhead_cursor->canvas_item) {
2176 if (_editor->session()) {
2177 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2178 _editor->_pending_locate_request = true;
2184 CursorDrag::aborted ()
2186 _editor->_dragging_playhead = false;
2187 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2190 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2191 : RegionDrag (e, i, p, v)
2197 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2199 Drag::start_grab (event, cursor);
2201 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2202 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2204 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2205 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2209 FadeInDrag::motion (GdkEvent* event, bool)
2211 nframes64_t fade_length;
2213 nframes64_t const pos = adjusted_current_frame (event);
2215 boost::shared_ptr<Region> region = _primary->region ();
2217 if (pos < (region->position() + 64)) {
2218 fade_length = 64; // this should be a minimum defined somewhere
2219 } else if (pos > region->last_frame()) {
2220 fade_length = region->length();
2222 fade_length = pos - region->position();
2225 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2227 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2233 tmp->reset_fade_in_shape_width (fade_length);
2236 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2240 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2242 if (!movement_occurred) {
2246 nframes64_t fade_length;
2248 nframes64_t const pos = adjusted_current_frame (event);
2250 boost::shared_ptr<Region> region = _primary->region ();
2252 if (pos < (region->position() + 64)) {
2253 fade_length = 64; // this should be a minimum defined somewhere
2254 } else if (pos > region->last_frame()) {
2255 fade_length = region->length();
2257 fade_length = pos - region->position();
2260 _editor->begin_reversible_command (_("change fade in length"));
2262 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2264 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2270 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2271 XMLNode &before = alist->get_state();
2273 tmp->audio_region()->set_fade_in_length (fade_length);
2274 tmp->audio_region()->set_fade_in_active (true);
2276 XMLNode &after = alist->get_state();
2277 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2280 _editor->commit_reversible_command ();
2284 FadeInDrag::aborted ()
2286 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2287 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2293 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2297 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2298 : RegionDrag (e, i, p, v)
2304 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2306 Drag::start_grab (event, cursor);
2308 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2309 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2311 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2312 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2316 FadeOutDrag::motion (GdkEvent* event, bool)
2318 nframes64_t fade_length;
2320 nframes64_t const pos = adjusted_current_frame (event);
2322 boost::shared_ptr<Region> region = _primary->region ();
2324 if (pos > (region->last_frame() - 64)) {
2325 fade_length = 64; // this should really be a minimum fade defined somewhere
2327 else if (pos < region->position()) {
2328 fade_length = region->length();
2331 fade_length = region->last_frame() - pos;
2334 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2336 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2342 tmp->reset_fade_out_shape_width (fade_length);
2345 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2349 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2351 if (!movement_occurred) {
2355 nframes64_t fade_length;
2357 nframes64_t const pos = adjusted_current_frame (event);
2359 boost::shared_ptr<Region> region = _primary->region ();
2361 if (pos > (region->last_frame() - 64)) {
2362 fade_length = 64; // this should really be a minimum fade defined somewhere
2364 else if (pos < region->position()) {
2365 fade_length = region->length();
2368 fade_length = region->last_frame() - pos;
2371 _editor->begin_reversible_command (_("change fade out length"));
2373 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2375 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2381 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2382 XMLNode &before = alist->get_state();
2384 tmp->audio_region()->set_fade_out_length (fade_length);
2385 tmp->audio_region()->set_fade_out_active (true);
2387 XMLNode &after = alist->get_state();
2388 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2391 _editor->commit_reversible_command ();
2395 FadeOutDrag::aborted ()
2397 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2398 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2404 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2408 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2411 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2414 _points.push_back (Gnome::Art::Point (0, 0));
2415 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2417 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2418 _line->property_width_pixels() = 1;
2419 _line->property_points () = _points;
2422 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2425 MarkerDrag::~MarkerDrag ()
2427 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2433 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2435 Drag::start_grab (event, cursor);
2439 Location *location = _editor->find_location_from_marker (_marker, is_start);
2440 _editor->_dragging_edit_point = true;
2442 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2444 update_item (location);
2446 // _drag_line->show();
2447 // _line->raise_to_top();
2450 _editor->show_verbose_time_cursor (location->start(), 10);
2452 _editor->show_verbose_time_cursor (location->end(), 10);
2455 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2458 case Selection::Toggle:
2459 _editor->selection->toggle (_marker);
2461 case Selection::Set:
2462 if (!_editor->selection->selected (_marker)) {
2463 _editor->selection->set (_marker);
2466 case Selection::Extend:
2468 Locations::LocationList ll;
2469 list<Marker*> to_add;
2471 _editor->selection->markers.range (s, e);
2472 s = min (_marker->position(), s);
2473 e = max (_marker->position(), e);
2476 if (e < max_frames) {
2479 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2480 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2481 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2484 to_add.push_back (lm->start);
2487 to_add.push_back (lm->end);
2491 if (!to_add.empty()) {
2492 _editor->selection->add (to_add);
2496 case Selection::Add:
2497 _editor->selection->add (_marker);
2501 /* set up copies for us to manipulate during the drag */
2503 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2504 Location *l = _editor->find_location_from_marker (*i, is_start);
2505 _copied_locations.push_back (new Location (*l));
2510 MarkerDrag::motion (GdkEvent* event, bool)
2512 nframes64_t f_delta = 0;
2514 bool move_both = false;
2516 Location *real_location;
2517 Location *copy_location = 0;
2519 nframes64_t const newframe = adjusted_current_frame (event);
2521 nframes64_t next = newframe;
2523 if (newframe == last_pointer_frame()) {
2527 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2531 MarkerSelection::iterator i;
2532 list<Location*>::iterator x;
2534 /* find the marker we're dragging, and compute the delta */
2536 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2537 x != _copied_locations.end() && i != _editor->selection->markers.end();
2543 if (marker == _marker) {
2545 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2550 if (real_location->is_mark()) {
2551 f_delta = newframe - copy_location->start();
2555 switch (marker->type()) {
2557 case Marker::LoopStart:
2558 case Marker::PunchIn:
2559 f_delta = newframe - copy_location->start();
2563 case Marker::LoopEnd:
2564 case Marker::PunchOut:
2565 f_delta = newframe - copy_location->end();
2568 /* what kind of marker is this ? */
2576 if (i == _editor->selection->markers.end()) {
2577 /* hmm, impossible - we didn't find the dragged marker */
2581 /* now move them all */
2583 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2584 x != _copied_locations.end() && i != _editor->selection->markers.end();
2590 /* call this to find out if its the start or end */
2592 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2596 if (real_location->locked()) {
2600 if (copy_location->is_mark()) {
2604 copy_location->set_start (copy_location->start() + f_delta);
2608 nframes64_t new_start = copy_location->start() + f_delta;
2609 nframes64_t new_end = copy_location->end() + f_delta;
2611 if (is_start) { // start-of-range marker
2614 copy_location->set_start (new_start);
2615 copy_location->set_end (new_end);
2616 } else if (new_start < copy_location->end()) {
2617 copy_location->set_start (new_start);
2619 _editor->snap_to (next, 1, true);
2620 copy_location->set_end (next);
2621 copy_location->set_start (newframe);
2624 } else { // end marker
2627 copy_location->set_end (new_end);
2628 copy_location->set_start (new_start);
2629 } else if (new_end > copy_location->start()) {
2630 copy_location->set_end (new_end);
2631 } else if (newframe > 0) {
2632 _editor->snap_to (next, -1, true);
2633 copy_location->set_start (next);
2634 copy_location->set_end (newframe);
2639 update_item (copy_location);
2641 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2644 lm->set_position (copy_location->start(), copy_location->end());
2648 assert (!_copied_locations.empty());
2650 _editor->show_verbose_time_cursor (newframe, 10);
2653 _editor->update_canvas_now ();
2658 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2660 if (!movement_occurred) {
2662 /* just a click, do nothing but finish
2663 off the selection process
2666 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2669 case Selection::Set:
2670 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2671 _editor->selection->set (_marker);
2675 case Selection::Toggle:
2676 case Selection::Extend:
2677 case Selection::Add:
2684 _editor->_dragging_edit_point = false;
2686 _editor->begin_reversible_command ( _("move marker") );
2687 XMLNode &before = _editor->session()->locations()->get_state();
2689 MarkerSelection::iterator i;
2690 list<Location*>::iterator x;
2693 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2694 x != _copied_locations.end() && i != _editor->selection->markers.end();
2697 Location * location = _editor->find_location_from_marker (*i, is_start);
2701 if (location->locked()) {
2705 if (location->is_mark()) {
2706 location->set_start ((*x)->start());
2708 location->set ((*x)->start(), (*x)->end());
2713 XMLNode &after = _editor->session()->locations()->get_state();
2714 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2715 _editor->commit_reversible_command ();
2721 MarkerDrag::aborted ()
2727 MarkerDrag::update_item (Location* location)
2729 double const x1 = _editor->frame_to_pixel (location->start());
2731 _points.front().set_x(x1);
2732 _points.back().set_x(x1);
2733 _line->property_points() = _points;
2736 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2738 _cumulative_x_drag (0),
2739 _cumulative_y_drag (0)
2741 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2747 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2749 Drag::start_grab (event, _editor->fader_cursor);
2751 // start the grab at the center of the control point so
2752 // the point doesn't 'jump' to the mouse after the first drag
2753 _time_axis_view_grab_x = _point->get_x();
2754 _time_axis_view_grab_y = _point->get_y();
2756 float const fraction = 1 - (_point->get_y() / _point->line().height());
2758 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2760 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2761 event->button.x + 10, event->button.y + 10);
2763 _editor->show_verbose_canvas_cursor ();
2767 ControlPointDrag::motion (GdkEvent* event, bool)
2769 double dx = _drags->current_pointer_x() - last_pointer_x();
2770 double dy = _drags->current_pointer_y() - last_pointer_y();
2772 if (event->button.state & Keyboard::SecondaryModifier) {
2777 /* coordinate in TimeAxisView's space */
2778 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2779 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2781 // calculate zero crossing point. back off by .01 to stay on the
2782 // positive side of zero
2783 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2785 // make sure we hit zero when passing through
2786 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2790 if (_x_constrained) {
2791 cx = _time_axis_view_grab_x;
2793 if (_y_constrained) {
2794 cy = _time_axis_view_grab_y;
2797 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2798 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2802 cy = min ((double) _point->line().height(), cy);
2804 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2806 if (!_x_constrained) {
2807 _editor->snap_to_with_modifier (cx_frames, event);
2810 float const fraction = 1.0 - (cy / _point->line().height());
2812 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2814 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2816 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2820 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2822 if (!movement_occurred) {
2826 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2827 _editor->reset_point_selection ();
2831 motion (event, false);
2833 _point->line().end_drag ();
2837 ControlPointDrag::aborted ()
2839 _point->line().reset ();
2843 ControlPointDrag::active (Editing::MouseMode m)
2845 if (m == Editing::MouseGain) {
2846 /* always active in mouse gain */
2850 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2851 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2854 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2857 _cumulative_y_drag (0)
2862 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2864 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2867 _item = &_line->grab_item ();
2869 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2870 origin, and ditto for y.
2873 double cx = event->button.x;
2874 double cy = event->button.y;
2876 _line->parent_group().w2i (cx, cy);
2878 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2883 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2884 /* no adjacent points */
2888 Drag::start_grab (event, _editor->fader_cursor);
2890 /* store grab start in parent frame */
2892 _time_axis_view_grab_x = cx;
2893 _time_axis_view_grab_y = cy;
2895 double fraction = 1.0 - (cy / _line->height());
2897 _line->start_drag_line (before, after, fraction);
2899 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2900 event->button.x + 10, event->button.y + 10);
2902 _editor->show_verbose_canvas_cursor ();
2906 LineDrag::motion (GdkEvent* event, bool)
2908 double dy = _drags->current_pointer_y() - last_pointer_y();
2910 if (event->button.state & Keyboard::SecondaryModifier) {
2914 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2916 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2919 cy = min ((double) _line->height(), cy);
2921 double const fraction = 1.0 - (cy / _line->height());
2925 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2931 /* we are ignoring x position for this drag, so we can just pass in anything */
2932 _line->drag_motion (0, fraction, true, push);
2934 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2938 LineDrag::finished (GdkEvent* event, bool)
2940 motion (event, false);
2945 LineDrag::aborted ()
2951 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2953 Drag::start_grab (event);
2954 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2958 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2965 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2967 nframes64_t grab = grab_frame ();
2968 if (Config->get_rubberbanding_snaps_to_grid ()) {
2969 _editor->snap_to_with_modifier (grab, event);
2972 /* base start and end on initial click position */
2982 if (_drags->current_pointer_y() < grab_y()) {
2983 y1 = _drags->current_pointer_y();
2986 y2 = _drags->current_pointer_y();
2991 if (start != end || y1 != y2) {
2993 double x1 = _editor->frame_to_pixel (start);
2994 double x2 = _editor->frame_to_pixel (end);
2996 _editor->rubberband_rect->property_x1() = x1;
2997 _editor->rubberband_rect->property_y1() = y1;
2998 _editor->rubberband_rect->property_x2() = x2;
2999 _editor->rubberband_rect->property_y2() = y2;
3001 _editor->rubberband_rect->show();
3002 _editor->rubberband_rect->raise_to_top();
3004 _editor->show_verbose_time_cursor (pf, 10);
3009 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3011 if (movement_occurred) {
3013 motion (event, false);
3016 if (_drags->current_pointer_y() < grab_y()) {
3017 y1 = _drags->current_pointer_y();
3020 y2 = _drags->current_pointer_y();
3025 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3028 _editor->begin_reversible_command (_("rubberband selection"));
3030 if (grab_frame() < last_pointer_frame()) {
3031 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3033 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3037 _editor->commit_reversible_command ();
3041 if (!getenv("ARDOUR_SAE")) {
3042 _editor->selection->clear_tracks();
3044 _editor->selection->clear_regions();
3045 _editor->selection->clear_points ();
3046 _editor->selection->clear_lines ();
3049 _editor->rubberband_rect->hide();
3053 RubberbandSelectDrag::aborted ()
3055 _editor->rubberband_rect->hide ();
3059 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3061 Drag::start_grab (event);
3063 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3067 TimeFXDrag::motion (GdkEvent* event, bool)
3069 RegionView* rv = _primary;
3071 nframes64_t const pf = adjusted_current_frame (event);
3073 if (pf > rv->region()->position()) {
3074 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3077 _editor->show_verbose_time_cursor (pf, 10);
3081 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3083 _primary->get_time_axis_view().hide_timestretch ();
3085 if (!movement_occurred) {
3089 if (last_pointer_frame() < _primary->region()->position()) {
3090 /* backwards drag of the left edge - not usable */
3094 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3096 float percentage = (double) newlen / (double) _primary->region()->length();
3098 #ifndef USE_RUBBERBAND
3099 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3100 if (_primary->region()->data_type() == DataType::AUDIO) {
3101 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3105 _editor->begin_reversible_command (_("timestretch"));
3107 // XXX how do timeFX on multiple regions ?
3112 if (!_editor->time_stretch (rs, percentage) == 0) {
3113 error << _("An error occurred while executing time stretch operation") << endmsg;
3118 TimeFXDrag::aborted ()
3120 _primary->get_time_axis_view().hide_timestretch ();
3125 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3127 Drag::start_grab (event);
3131 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3133 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3137 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3139 if (movement_occurred && _editor->session()) {
3140 /* make sure we stop */
3141 _editor->session()->request_transport_speed (0.0);
3146 ScrubDrag::aborted ()
3151 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3155 , _original_pointer_time_axis (-1)
3156 , _last_pointer_time_axis (-1)
3162 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3164 nframes64_t start = 0;
3165 nframes64_t end = 0;
3167 if (_editor->session() == 0) {
3171 Gdk::Cursor* cursor = 0;
3173 switch (_operation) {
3174 case CreateSelection:
3175 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3180 cursor = _editor->selector_cursor;
3181 Drag::start_grab (event, cursor);
3184 case SelectionStartTrim:
3185 if (_editor->clicked_axisview) {
3186 _editor->clicked_axisview->order_selection_trims (_item, true);
3188 Drag::start_grab (event, _editor->trimmer_cursor);
3189 start = _editor->selection->time[_editor->clicked_selection].start;
3190 _pointer_frame_offset = grab_frame() - start;
3193 case SelectionEndTrim:
3194 if (_editor->clicked_axisview) {
3195 _editor->clicked_axisview->order_selection_trims (_item, false);
3197 Drag::start_grab (event, _editor->trimmer_cursor);
3198 end = _editor->selection->time[_editor->clicked_selection].end;
3199 _pointer_frame_offset = grab_frame() - end;
3203 start = _editor->selection->time[_editor->clicked_selection].start;
3204 Drag::start_grab (event, cursor);
3205 _pointer_frame_offset = grab_frame() - start;
3209 if (_operation == SelectionMove) {
3210 _editor->show_verbose_time_cursor (start, 10);
3212 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3215 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3219 SelectionDrag::motion (GdkEvent* event, bool first_move)
3221 nframes64_t start = 0;
3222 nframes64_t end = 0;
3225 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3226 if (pending_time_axis.first == 0) {
3230 nframes64_t const pending_position = adjusted_current_frame (event);
3232 /* only alter selection if things have changed */
3234 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3238 switch (_operation) {
3239 case CreateSelection:
3241 nframes64_t grab = grab_frame ();
3244 _editor->snap_to (grab);
3247 if (pending_position < grab_frame()) {
3248 start = pending_position;
3251 end = pending_position;
3255 /* first drag: Either add to the selection
3256 or create a new selection
3262 /* adding to the selection */
3263 _editor->selection->add (_editor->clicked_axisview);
3264 _editor->clicked_selection = _editor->selection->add (start, end);
3269 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3270 _editor->selection->set (_editor->clicked_axisview);
3273 _editor->clicked_selection = _editor->selection->set (start, end);
3277 /* select the track that we're in */
3278 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3279 _editor->selection->add (pending_time_axis.first);
3280 _added_time_axes.push_back (pending_time_axis.first);
3283 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3284 tracks that we selected in the first place.
3287 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3288 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3290 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3291 while (i != _added_time_axes.end()) {
3293 list<TimeAxisView*>::iterator tmp = i;
3296 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3297 _editor->selection->remove (*i);
3298 _added_time_axes.remove (*i);
3307 case SelectionStartTrim:
3309 start = _editor->selection->time[_editor->clicked_selection].start;
3310 end = _editor->selection->time[_editor->clicked_selection].end;
3312 if (pending_position > end) {
3315 start = pending_position;
3319 case SelectionEndTrim:
3321 start = _editor->selection->time[_editor->clicked_selection].start;
3322 end = _editor->selection->time[_editor->clicked_selection].end;
3324 if (pending_position < start) {
3327 end = pending_position;
3334 start = _editor->selection->time[_editor->clicked_selection].start;
3335 end = _editor->selection->time[_editor->clicked_selection].end;
3337 length = end - start;
3339 start = pending_position;
3340 _editor->snap_to (start);
3342 end = start + length;
3347 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3348 _editor->start_canvas_autoscroll (1, 0);
3352 _editor->selection->replace (_editor->clicked_selection, start, end);
3355 if (_operation == SelectionMove) {
3356 _editor->show_verbose_time_cursor(start, 10);
3358 _editor->show_verbose_time_cursor(pending_position, 10);
3363 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3365 Session* s = _editor->session();
3367 if (movement_occurred) {
3368 motion (event, false);
3369 /* XXX this is not object-oriented programming at all. ick */
3370 if (_editor->selection->time.consolidate()) {
3371 _editor->selection->TimeChanged ();
3374 /* XXX what if its a music time selection? */
3375 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3376 s->request_play_range (&_editor->selection->time, true);
3381 /* just a click, no pointer movement.*/
3383 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3384 _editor->selection->clear_time();
3387 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3388 _editor->selection->set (_editor->clicked_axisview);
3391 if (s && s->get_play_range () && s->transport_rolling()) {
3392 s->request_stop (false, false);
3397 _editor->stop_canvas_autoscroll ();
3401 SelectionDrag::aborted ()
3406 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3411 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3412 _drag_rect->hide ();
3414 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3415 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3419 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3421 if (_editor->session() == 0) {
3425 Gdk::Cursor* cursor = 0;
3427 if (!_editor->temp_location) {
3428 _editor->temp_location = new Location;
3431 switch (_operation) {
3432 case CreateRangeMarker:
3433 case CreateTransportMarker:
3434 case CreateCDMarker:
3436 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3441 cursor = _editor->selector_cursor;
3445 Drag::start_grab (event, cursor);
3447 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3451 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3453 nframes64_t start = 0;
3454 nframes64_t end = 0;
3455 ArdourCanvas::SimpleRect *crect;
3457 switch (_operation) {
3458 case CreateRangeMarker:
3459 crect = _editor->range_bar_drag_rect;
3461 case CreateTransportMarker:
3462 crect = _editor->transport_bar_drag_rect;
3464 case CreateCDMarker:
3465 crect = _editor->cd_marker_bar_drag_rect;
3468 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3473 nframes64_t const pf = adjusted_current_frame (event);
3475 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3476 nframes64_t grab = grab_frame ();
3477 _editor->snap_to (grab);
3479 if (pf < grab_frame()) {
3487 /* first drag: Either add to the selection
3488 or create a new selection.
3493 _editor->temp_location->set (start, end);
3497 update_item (_editor->temp_location);
3499 //_drag_rect->raise_to_top();
3504 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3505 _editor->start_canvas_autoscroll (1, 0);
3509 _editor->temp_location->set (start, end);
3511 double x1 = _editor->frame_to_pixel (start);
3512 double x2 = _editor->frame_to_pixel (end);
3513 crect->property_x1() = x1;
3514 crect->property_x2() = x2;
3516 update_item (_editor->temp_location);
3519 _editor->show_verbose_time_cursor (pf, 10);
3524 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3526 Location * newloc = 0;
3530 if (movement_occurred) {
3531 motion (event, false);
3534 switch (_operation) {
3535 case CreateRangeMarker:
3536 case CreateCDMarker:
3538 _editor->begin_reversible_command (_("new range marker"));
3539 XMLNode &before = _editor->session()->locations()->get_state();
3540 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3541 if (_operation == CreateCDMarker) {
3542 flags = Location::IsRangeMarker | Location::IsCDMarker;
3543 _editor->cd_marker_bar_drag_rect->hide();
3546 flags = Location::IsRangeMarker;
3547 _editor->range_bar_drag_rect->hide();
3549 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3550 _editor->session()->locations()->add (newloc, true);
3551 XMLNode &after = _editor->session()->locations()->get_state();
3552 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3553 _editor->commit_reversible_command ();
3557 case CreateTransportMarker:
3558 // popup menu to pick loop or punch
3559 _editor->new_transport_marker_context_menu (&event->button, _item);
3563 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3565 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3570 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3572 if (end == max_frames) {
3573 end = _editor->session()->current_end_frame ();
3576 if (start == max_frames) {
3577 start = _editor->session()->current_start_frame ();
3580 switch (_editor->mouse_mode) {
3582 /* find the two markers on either side and then make the selection from it */
3583 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3587 /* find the two markers on either side of the click and make the range out of it */
3588 _editor->selection->set (start, end);
3597 _editor->stop_canvas_autoscroll ();
3601 RangeMarkerBarDrag::aborted ()
3607 RangeMarkerBarDrag::update_item (Location* location)
3609 double const x1 = _editor->frame_to_pixel (location->start());
3610 double const x2 = _editor->frame_to_pixel (location->end());
3612 _drag_rect->property_x1() = x1;
3613 _drag_rect->property_x2() = x2;
3617 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3619 Drag::start_grab (event, _editor->zoom_cursor);
3620 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3624 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3629 nframes64_t const pf = adjusted_current_frame (event);
3631 nframes64_t grab = grab_frame ();
3632 _editor->snap_to_with_modifier (grab, event);
3634 /* base start and end on initial click position */
3646 _editor->zoom_rect->show();
3647 _editor->zoom_rect->raise_to_top();
3650 _editor->reposition_zoom_rect(start, end);
3652 _editor->show_verbose_time_cursor (pf, 10);
3657 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3659 if (movement_occurred) {
3660 motion (event, false);
3662 if (grab_frame() < last_pointer_frame()) {
3663 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3665 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3668 _editor->temporal_zoom_to_frame (false, grab_frame());
3670 temporal_zoom_step (false);
3671 center_screen (grab_frame());
3675 _editor->zoom_rect->hide();
3679 MouseZoomDrag::aborted ()
3681 _editor->zoom_rect->hide ();
3684 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3687 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3688 region = &cnote->region_view();
3692 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3694 Drag::start_grab (event);
3697 drag_delta_note = 0;
3702 event_x = _drags->current_pointer_x();
3703 event_y = _drags->current_pointer_y();
3705 _item->property_parent().get_value()->w2i(event_x, event_y);
3707 last_x = region->snap_to_pixel(event_x);
3710 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3712 if (!(was_selected = cnote->selected())) {
3714 /* tertiary-click means extend selection - we'll do that on button release,
3715 so don't add it here, because otherwise we make it hard to figure
3716 out the "extend-to" range.
3719 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3722 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3725 region->note_selected (cnote, true);
3727 region->unique_select (cnote);
3734 NoteDrag::motion (GdkEvent*, bool)
3736 MidiStreamView* streamview = region->midi_stream_view();
3740 event_x = _drags->current_pointer_x();
3741 event_y = _drags->current_pointer_y();
3743 _item->property_parent().get_value()->w2i(event_x, event_y);
3745 event_x = region->snap_to_pixel(event_x);
3747 double dx = event_x - last_x;
3748 double dy = event_y - last_y;
3753 // Snap to note rows
3755 if (abs (dy) < streamview->note_height()) {
3758 int8_t this_delta_note;
3760 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3762 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3764 drag_delta_note -= this_delta_note;
3765 dy = streamview->note_height() * this_delta_note;
3766 last_y = last_y + dy;
3770 region->move_selection (dx, dy);
3772 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3774 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3775 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3776 _editor->show_verbose_canvas_cursor_with (buf);
3781 NoteDrag::finished (GdkEvent* ev, bool moved)
3783 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3786 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3789 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3791 region->note_deselected (cnote);
3794 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3795 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3797 if (!extend && !add && region->selection_size() > 1) {
3798 region->unique_select(cnote);
3799 } else if (extend) {
3800 region->note_selected (cnote, true, true);
3802 /* it was added during button press */
3807 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3812 NoteDrag::aborted ()
3817 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3820 , _nothing_to_drag (false)
3822 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3825 _line = _atav->line ();
3829 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3831 Drag::start_grab (event, cursor);
3833 list<ControlPoint*> points;
3835 XMLNode* state = &_line->get_state ();
3837 if (_ranges.empty()) {
3839 uint32_t const N = _line->npoints ();
3840 for (uint32_t i = 0; i < N; ++i) {
3841 points.push_back (_line->nth (i));
3846 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3847 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3849 /* fade into and out of the region that we're dragging;
3850 64 samples length plucked out of thin air.
3852 nframes64_t const h = (j->start + j->end) / 2;
3853 nframes64_t a = j->start + 64;
3857 nframes64_t b = j->end - 64;
3862 the_list->add (j->start, the_list->eval (j->start));
3863 _line->add_always_in_view (j->start);
3864 the_list->add (a, the_list->eval (a));
3865 _line->add_always_in_view (a);
3866 the_list->add (b, the_list->eval (b));
3867 _line->add_always_in_view (b);
3868 the_list->add (j->end, the_list->eval (j->end));
3869 _line->add_always_in_view (j->end);
3872 uint32_t const N = _line->npoints ();
3873 for (uint32_t i = 0; i < N; ++i) {
3875 ControlPoint* p = _line->nth (i);
3877 list<AudioRange>::const_iterator j = _ranges.begin ();
3878 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3882 if (j != _ranges.end()) {
3883 points.push_back (p);
3888 if (points.empty()) {
3889 _nothing_to_drag = true;
3893 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3897 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3899 if (_nothing_to_drag) {
3903 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3905 /* we are ignoring x position for this drag, so we can just pass in anything */
3906 _line->drag_motion (0, f, true, false);
3910 AutomationRangeDrag::finished (GdkEvent* event, bool)
3912 if (_nothing_to_drag) {
3916 motion (event, false);
3918 _line->clear_always_in_view ();
3922 AutomationRangeDrag::aborted ()
3924 _line->clear_always_in_view ();