2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #define __STDC_LIMIT_MACROS 1
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
31 #include "audio_region_view.h"
32 #include "midi_region_view.h"
33 #include "ardour_ui.h"
34 #include "gui_thread.h"
35 #include "control_point.h"
37 #include "region_gain_line.h"
38 #include "editor_drag.h"
39 #include "audio_time_axis.h"
40 #include "midi_time_axis.h"
41 #include "canvas-note.h"
42 #include "selection.h"
43 #include "midi_selection.h"
44 #include "automation_time_axis.h"
47 using namespace ARDOUR;
50 using namespace Editing;
51 using namespace ArdourCanvas;
53 using Gtkmm2ext::Keyboard;
55 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
57 DragManager::DragManager (Editor* e)
60 , _current_pointer_frame (0)
65 DragManager::~DragManager ()
70 /** Call abort for each active drag */
76 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
87 DragManager::add (Drag* d)
89 d->set_manager (this);
94 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
96 assert (_drags.empty ());
97 d->set_manager (this);
103 DragManager::start_grab (GdkEvent* e)
105 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
107 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 (*i)->start_grab (e);
112 /** Call end_grab for each active drag.
113 * @return true if any drag reported movement having occurred.
116 DragManager::end_grab (GdkEvent* e)
121 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
122 bool const t = (*i)->end_grab (e);
137 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
141 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
143 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144 bool const t = (*i)->motion_handler (e, from_autoscroll);
155 DragManager::have_item (ArdourCanvas::Item* i) const
157 list<Drag*>::const_iterator j = _drags.begin ();
158 while (j != _drags.end() && (*j)->item () != i) {
162 return j != _drags.end ();
165 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
168 , _pointer_frame_offset (0)
169 , _move_threshold_passed (false)
171 , _last_pointer_frame (0)
177 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
183 cursor = _editor->which_grabber_cursor ();
186 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
190 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
193 cursor = _editor->which_grabber_cursor ();
196 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
198 if (Keyboard::is_button2_event (&event->button)) {
199 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
200 _y_constrained = true;
201 _x_constrained = false;
203 _y_constrained = false;
204 _x_constrained = true;
207 _x_constrained = false;
208 _y_constrained = false;
211 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
212 _grab_frame = adjusted_frame (_grab_frame, event);
213 _last_pointer_frame = _grab_frame;
214 _last_pointer_x = _grab_x;
215 _last_pointer_y = _grab_y;
217 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
221 if (_editor->session() && _editor->session()->transport_rolling()) {
224 _was_rolling = false;
227 switch (_editor->snap_type()) {
228 case SnapToRegionStart:
229 case SnapToRegionEnd:
230 case SnapToRegionSync:
231 case SnapToRegionBoundary:
232 _editor->build_region_boundary_cache ();
239 /** Call to end a drag `successfully'. Ungrabs item and calls
240 * subclass' finished() method.
242 * @param event GDK event, or 0.
243 * @return true if some movement occurred, otherwise false.
246 Drag::end_grab (GdkEvent* event)
248 _editor->stop_canvas_autoscroll ();
250 _item->ungrab (event ? event->button.time : 0);
252 finished (event, _move_threshold_passed);
254 _editor->hide_verbose_canvas_cursor();
256 return _move_threshold_passed;
260 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
264 if (f > _pointer_frame_offset) {
265 pos = f - _pointer_frame_offset;
269 _editor->snap_to_with_modifier (pos, event);
276 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
278 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
282 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
284 /* check to see if we have moved in any way that matters since the last motion event */
285 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
286 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
290 pair<nframes64_t, int> const threshold = move_threshold ();
292 bool const old_move_threshold_passed = _move_threshold_passed;
294 if (!from_autoscroll && !_move_threshold_passed) {
296 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
297 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
299 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
302 if (active (_editor->mouse_mode) && _move_threshold_passed) {
304 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
305 if (!from_autoscroll) {
306 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
309 motion (event, _move_threshold_passed != old_move_threshold_passed);
311 _last_pointer_x = _drags->current_pointer_x ();
312 _last_pointer_y = _drags->current_pointer_y ();
313 _last_pointer_frame = adjusted_current_frame (event);
322 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
332 _editor->stop_canvas_autoscroll ();
333 _editor->hide_verbose_canvas_cursor ();
336 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
340 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
341 _views.push_back (DraggingView (*i));
344 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
348 RegionDrag::region_going_away (RegionView* v)
350 list<DraggingView>::iterator i = _views.begin ();
351 while (i != _views.end() && i->view != v) {
355 if (i != _views.end()) {
360 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
361 : RegionDrag (e, i, p, v),
372 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
374 Drag::start_grab (event);
376 _editor->show_verbose_time_cursor (_last_frame_position, 10);
379 RegionMotionDrag::TimeAxisViewSummary
380 RegionMotionDrag::get_time_axis_view_summary ()
382 int32_t children = 0;
383 TimeAxisViewSummary sum;
385 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
387 /* get a bitmask representing the visible tracks */
389 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
390 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
391 TimeAxisView::Children children_list;
393 /* zeroes are audio/MIDI tracks. ones are other types. */
395 if (!rtv->hidden()) {
397 if (!rtv->is_track()) {
398 /* not an audio nor MIDI track */
399 sum.tracks = sum.tracks |= (0x01 << rtv->order());
402 sum.height_list[rtv->order()] = (*i)->current_height();
405 if ((children_list = rtv->get_child_list()).size() > 0) {
406 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
407 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
408 sum.height_list[rtv->order() + children] = (*j)->current_height();
419 RegionMotionDrag::compute_y_delta (
420 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
421 int32_t last_pointer_layer, int32_t current_pointer_layer,
422 TimeAxisViewSummary const & tavs,
423 int32_t* pointer_order_span, int32_t* pointer_layer_span,
424 int32_t* canvas_pointer_order_span
428 *pointer_order_span = 0;
429 *pointer_layer_span = 0;
433 bool clamp_y_axis = false;
435 /* the change in track order between this callback and the last */
436 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
437 /* the change in layer between this callback and the last;
438 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
439 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
441 if (*pointer_order_span != 0) {
443 /* find the actual pointer span, in terms of the number of visible tracks;
444 to do this, we reduce |pointer_order_span| by the number of hidden tracks
447 *canvas_pointer_order_span = *pointer_order_span;
448 if (last_pointer_view->order() >= current_pointer_view->order()) {
449 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
450 if (tavs.height_list[y] == 0) {
451 *canvas_pointer_order_span--;
455 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
456 if (tavs.height_list[y] == 0) {
457 *canvas_pointer_order_span++;
462 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
464 RegionView* rv = i->view;
466 if (rv->region()->locked()) {
470 double ix1, ix2, iy1, iy2;
471 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
472 rv->get_canvas_frame()->i2w (ix1, iy1);
473 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
475 /* get the new trackview for this particular region */
476 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
478 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
480 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
481 as surely this is a per-region thing... */
483 clamp_y_axis = y_movement_disallowed (
484 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
492 } else if (_dest_trackview == current_pointer_view) {
494 if (current_pointer_layer == last_pointer_layer) {
495 /* No movement; clamp */
501 _dest_trackview = current_pointer_view;
502 _dest_layer = current_pointer_layer;
510 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
512 /* compute the amount of pointer motion in frames, and where
513 the region would be if we moved it by that much.
515 *pending_region_position = adjusted_current_frame (event);
517 nframes64_t sync_frame;
518 nframes64_t sync_offset;
521 sync_offset = _primary->region()->sync_offset (sync_dir);
523 /* we don't handle a sync point that lies before zero.
525 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
527 sync_frame = *pending_region_position + (sync_dir*sync_offset);
529 _editor->snap_to_with_modifier (sync_frame, event);
531 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
534 *pending_region_position = _last_frame_position;
537 if (*pending_region_position > max_frames - _primary->region()->length()) {
538 *pending_region_position = _last_frame_position;
543 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
545 /* now compute the canvas unit distance we need to move the regionview
546 to make it appear at the new location.
549 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
551 if (*pending_region_position <= _last_frame_position) {
553 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
555 RegionView* rv = i->view;
557 // If any regionview is at zero, we need to know so we can stop further leftward motion.
559 double ix1, ix2, iy1, iy2;
560 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
561 rv->get_canvas_frame()->i2w (ix1, iy1);
563 if (-x_delta > ix1 + _editor->_horizontal_position) {
565 *pending_region_position = _last_frame_position;
572 _last_frame_position = *pending_region_position;
579 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
583 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
585 vector<int32_t>::iterator j;
587 /* *pointer* variables reflect things about the pointer; as we may be moving
588 multiple regions, much detail must be computed per-region */
590 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
591 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
592 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
593 is always 0 regardless of what the region's "real" layer is */
594 RouteTimeAxisView* current_pointer_view;
595 layer_t current_pointer_layer;
596 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
600 /* TimeAxisView that we were pointing at last time we entered this method */
601 TimeAxisView const * const last_pointer_view = _dest_trackview;
602 /* the order of the track that we were pointing at last time we entered this method */
603 int32_t const last_pointer_order = last_pointer_view->order ();
604 /* the layer that we were pointing at last time we entered this method */
605 layer_t const last_pointer_layer = _dest_layer;
607 int32_t pointer_order_span;
608 int32_t pointer_layer_span;
609 int32_t canvas_pointer_order_span;
611 bool const clamp_y_axis = compute_y_delta (
612 last_pointer_view, current_pointer_view,
613 last_pointer_layer, current_pointer_layer, tavs,
614 &pointer_order_span, &pointer_layer_span,
615 &canvas_pointer_order_span
618 nframes64_t pending_region_position;
619 double const x_delta = compute_x_delta (event, &pending_region_position);
621 /*************************************************************
623 ************************************************************/
625 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
626 /* haven't reached next snap point, and we're not switching
627 trackviews nor layers. nothing to do.
632 /*************************************************************
634 ************************************************************/
636 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
638 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
640 RegionView* rv = i->view;
642 if (rv->region()->locked()) {
646 /* here we are calculating the y distance from the
647 top of the first track view to the top of the region
648 area of the track view that we're working on */
650 /* this x value is just a dummy value so that we have something
655 /* distance from the top of this track view to the region area
656 of our track view is always 1 */
660 /* convert to world coordinates, ie distance from the top of
663 rv->get_canvas_frame()->i2w (ix1, iy1);
665 /* compensate for the ruler section and the vertical scrollbar position */
666 iy1 += _editor->get_trackview_group_vertical_offset ();
670 // hide any dependent views
672 rv->get_time_axis_view().hide_dependent_views (*rv);
675 reparent to a non scrolling group so that we can keep the
676 region selection above all time axis views.
677 reparenting means we have to move the rv as the two
678 parent groups have different coordinates.
681 rv->get_canvas_group()->property_y() = iy1 - 1;
682 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
684 rv->fake_set_opaque (true);
687 /* current view for this particular region */
688 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
689 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
691 if (pointer_order_span != 0 && !clamp_y_axis) {
693 /* INTER-TRACK MOVEMENT */
695 /* move through the height list to the track that the region is currently on */
696 vector<int32_t>::iterator j = tavs.height_list.begin ();
698 while (j != tavs.height_list.end () && x != rtv->order ()) {
704 int32_t temp_pointer_order_span = canvas_pointer_order_span;
706 if (j != tavs.height_list.end ()) {
708 /* Account for layers in the original and
709 destination tracks. If we're moving around in layers we assume
710 that only one track is involved, so it's ok to use *pointer*
713 StreamView* lv = last_pointer_view->view ();
716 /* move to the top of the last trackview */
717 if (lv->layer_display () == Stacked) {
718 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
721 StreamView* cv = current_pointer_view->view ();
724 /* move to the right layer on the current trackview */
725 if (cv->layer_display () == Stacked) {
726 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
729 /* And for being on a non-topmost layer on the new
732 while (temp_pointer_order_span > 0) {
733 /* we're moving up canvas-wise,
734 so we need to find the next track height
736 if (j != tavs.height_list.begin()) {
740 if (x != last_pointer_order) {
742 ++temp_pointer_order_span;
747 temp_pointer_order_span--;
750 while (temp_pointer_order_span < 0) {
754 if (x != last_pointer_order) {
756 --temp_pointer_order_span;
760 if (j != tavs.height_list.end()) {
764 temp_pointer_order_span++;
768 /* find out where we'll be when we move and set height accordingly */
770 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
771 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
772 rv->set_height (temp_rtv->view()->child_height());
774 /* if you un-comment the following, the region colours will follow
775 the track colours whilst dragging; personally
776 i think this can confuse things, but never mind.
779 //const GdkColor& col (temp_rtv->view->get_region_color());
780 //rv->set_color (const_cast<GdkColor&>(col));
784 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
786 /* INTER-LAYER MOVEMENT in the same track */
787 y_delta = rtv->view()->child_height () * pointer_layer_span;
792 _editor->mouse_brush_insert_region (rv, pending_region_position);
794 rv->move (x_delta, y_delta);
797 } /* foreach region */
799 _total_x_delta += x_delta;
802 _editor->cursor_group->raise_to_top();
805 if (x_delta != 0 && !_brushing) {
806 _editor->show_verbose_time_cursor (_last_frame_position, 10);
811 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
813 if (_copy && first_move) {
814 copy_regions (event);
817 RegionMotionDrag::motion (event, first_move);
821 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
823 vector<RegionView*> copies;
824 boost::shared_ptr<Track> tr;
825 boost::shared_ptr<Playlist> from_playlist;
826 boost::shared_ptr<Playlist> to_playlist;
827 RegionSelection new_views;
828 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
829 PlaylistSet modified_playlists;
830 PlaylistSet frozen_playlists;
831 list <sigc::connection> modified_playlist_connections;
832 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
833 nframes64_t drag_delta;
834 bool changed_tracks, changed_position;
835 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
836 RouteTimeAxisView* source_tv;
837 vector<StatefulDiffCommand*> sdc;
839 if (!movement_occurred) {
845 /* all changes were made during motion event handlers */
848 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
849 copies.push_back (i->view);
856 /* reverse this here so that we have the correct logic to finalize
860 if (Config->get_edit_mode() == Lock) {
861 _x_constrained = !_x_constrained;
865 if (_x_constrained) {
866 _editor->begin_reversible_command (_("fixed time region copy"));
868 _editor->begin_reversible_command (_("region copy"));
871 if (_x_constrained) {
872 _editor->begin_reversible_command (_("fixed time region drag"));
874 _editor->begin_reversible_command (_("region drag"));
878 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
879 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
881 drag_delta = _primary->region()->position() - _last_frame_position;
883 _editor->update_canvas_now ();
885 /* make a list of where each region ended up */
886 final = find_time_axis_views_and_layers ();
888 cerr << "Iterate over " << _views.size() << " views\n";
890 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
892 RegionView* rv = i->view;
893 RouteTimeAxisView* dest_rtv = final[rv].first;
894 layer_t dest_layer = final[rv].second;
898 from_playlist.reset ();
899 to_playlist.reset ();
901 if (rv->region()->locked()) {
906 if (changed_position && !_x_constrained) {
907 where = rv->region()->position() - drag_delta;
909 where = rv->region()->position();
912 boost::shared_ptr<Region> new_region;
915 /* we already made a copy */
916 new_region = rv->region();
918 /* undo the previous hide_dependent_views so that xfades don't
919 disappear on copying regions
922 //rv->get_time_axis_view().reveal_dependent_views (*rv);
924 } else if (changed_tracks && dest_rtv->playlist()) {
925 new_region = RegionFactory::create (rv->region());
928 if (changed_tracks || _copy) {
930 to_playlist = dest_rtv->playlist();
937 _editor->latest_regionviews.clear ();
939 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
941 insert_result = modified_playlists.insert (to_playlist);
943 if (insert_result.second) {
944 to_playlist->clear_history ();
947 cerr << "To playlist " << to_playlist->name() << " region history contains "
948 << to_playlist->region_list().change().added.size() << " adds and "
949 << to_playlist->region_list().change().removed.size() << " removes\n";
951 cerr << "Adding new region " << new_region->id() << " based on "
952 << rv->region()->id() << endl;
954 to_playlist->add_region (new_region, where);
956 if (dest_rtv->view()->layer_display() == Stacked) {
957 new_region->set_layer (dest_layer);
958 new_region->set_pending_explicit_relayer (true);
963 if (!_editor->latest_regionviews.empty()) {
964 // XXX why just the first one ? we only expect one
965 // commented out in nick_m's canvas reworking. is that intended?
966 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
967 new_views.push_back (_editor->latest_regionviews.front());
971 rv->region()->clear_history ();
974 motion on the same track. plonk the previously reparented region
975 back to its original canvas group (its streamview).
976 No need to do anything for copies as they are fake regions which will be deleted.
979 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
980 rv->get_canvas_group()->property_y() = i->initial_y;
981 rv->get_time_axis_view().reveal_dependent_views (*rv);
983 /* just change the model */
985 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
987 if (dest_rtv->view()->layer_display() == Stacked) {
988 rv->region()->set_layer (dest_layer);
989 rv->region()->set_pending_explicit_relayer (true);
992 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
994 frozen_insert_result = frozen_playlists.insert(playlist);
996 if (frozen_insert_result.second) {
1000 cerr << "Moving region " << rv->region()->id() << endl;
1002 rv->region()->set_position (where, (void*) this);
1004 sdc.push_back (new StatefulDiffCommand (rv->region()));
1007 if (changed_tracks && !_copy) {
1009 /* get the playlist where this drag started. we can't use rv->region()->playlist()
1010 because we may have copied the region and it has not been attached to a playlist.
1013 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
1014 tr = source_tv->track();
1015 from_playlist = tr->playlist();
1019 assert (from_playlist);
1021 /* moved to a different audio track, without copying */
1023 /* the region that used to be in the old playlist is not
1024 moved to the new one - we use a copy of it. as a result,
1025 any existing editor for the region should no longer be
1029 rv->hide_region_editor();
1030 rv->fake_set_opaque (false);
1032 /* remove the region from the old playlist */
1034 insert_result = modified_playlists.insert (from_playlist);
1036 if (insert_result.second) {
1037 from_playlist->clear_history ();
1040 cerr << "From playlist " << from_playlist->name() << " region history contains "
1041 << from_playlist->region_list().change().added.size() << " adds and "
1042 << from_playlist->region_list().change().removed.size() << " removes\n";
1044 cerr << "removing region " << rv->region() << endl;
1046 from_playlist->remove_region (rv->region());
1048 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1049 was selected in all of them, then removing it from a playlist will have removed all
1050 trace of it from the selection (i.e. there were N regions selected, we removed 1,
1051 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1052 corresponding regionview, and the selection is now empty).
1054 this could have invalidated any and all iterators into the region selection.
1056 the heuristic we use here is: if the region selection is empty, break out of the loop
1057 here. if the region selection is not empty, then restart the loop because we know that
1058 we must have removed at least the region(view) we've just been working on as well as any
1059 that we processed on previous iterations.
1061 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
1062 we can just iterate.
1065 if (_views.empty()) {
1067 sdc.push_back (new StatefulDiffCommand (to_playlist));
1068 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1071 if (from_playlist && (from_playlist != to_playlist)) {
1072 sdc.push_back (new StatefulDiffCommand (from_playlist));
1073 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1085 copies.push_back (rv);
1088 cerr << "Done with TV, top = " << to_playlist << " from = " << from_playlist << endl;
1091 sdc.push_back (new StatefulDiffCommand (to_playlist));
1092 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1095 if (from_playlist && (from_playlist != to_playlist)) {
1096 sdc.push_back (new StatefulDiffCommand (from_playlist));
1097 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1102 if we've created new regions either by copying or moving
1103 to a new track, we want to replace the old selection with the new ones
1106 if (new_views.size() > 0) {
1107 _editor->selection->set (new_views);
1110 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1115 for (vector<StatefulDiffCommand*>::iterator i = sdc.begin(); i != sdc.end(); ++i) {
1116 _editor->session()->add_command (*i);
1119 _editor->commit_reversible_command ();
1121 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1127 RegionMoveDrag::aborted ()
1131 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1138 RegionMotionDrag::aborted ();
1143 RegionMotionDrag::aborted ()
1145 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1146 RegionView* rv = i->view;
1147 TimeAxisView* tv = &(rv->get_time_axis_view ());
1148 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1150 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1151 rv->get_canvas_group()->property_y() = 0;
1152 rv->get_time_axis_view().reveal_dependent_views (*rv);
1153 rv->fake_set_opaque (false);
1154 rv->move (-_total_x_delta, 0);
1155 rv->set_height (rtv->view()->child_height ());
1158 _editor->update_canvas_now ();
1163 RegionMotionDrag::x_move_allowed () const
1165 if (Config->get_edit_mode() == Lock) {
1166 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1167 return _x_constrained;
1170 return !_x_constrained;
1174 RegionMotionDrag::copy_regions (GdkEvent* event)
1176 /* duplicate the regionview(s) and region(s) */
1178 list<DraggingView> new_regionviews;
1180 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1182 RegionView* rv = i->view;
1183 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1184 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1186 const boost::shared_ptr<const Region> original = rv->region();
1187 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1188 region_copy->set_position (original->position(), this);
1192 boost::shared_ptr<AudioRegion> audioregion_copy
1193 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1195 nrv = new AudioRegionView (*arv, audioregion_copy);
1197 boost::shared_ptr<MidiRegion> midiregion_copy
1198 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1199 nrv = new MidiRegionView (*mrv, midiregion_copy);
1204 nrv->get_canvas_group()->show ();
1205 new_regionviews.push_back (DraggingView (nrv));
1207 /* swap _primary to the copy */
1209 if (rv == _primary) {
1213 /* ..and deselect the one we copied */
1215 rv->set_selected (false);
1218 if (new_regionviews.empty()) {
1222 /* reflect the fact that we are dragging the copies */
1224 _views = new_regionviews;
1226 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1229 sync the canvas to what we think is its current state
1230 without it, the canvas seems to
1231 "forget" to update properly after the upcoming reparent()
1232 ..only if the mouse is in rapid motion at the time of the grab.
1233 something to do with regionview creation taking so long?
1235 _editor->update_canvas_now();
1239 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1241 /* Which trackview is this ? */
1243 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1244 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1245 (*layer) = tvp.second;
1247 if (*tv && (*tv)->layer_display() == Overlaid) {
1251 /* The region motion is only processed if the pointer is over
1255 if (!(*tv) || !(*tv)->is_track()) {
1256 /* To make sure we hide the verbose canvas cursor when the mouse is
1257 not held over and audiotrack.
1259 _editor->hide_verbose_canvas_cursor ();
1266 /** @param new_order New track order.
1267 * @param old_order Old track order.
1268 * @param visible_y_low Lowest visible order.
1269 * @return true if y movement should not happen, otherwise false.
1272 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1274 if (new_order != old_order) {
1276 /* this isn't the pointer track */
1280 /* moving up the canvas */
1281 if ( (new_order - y_span) >= tavs.visible_y_low) {
1285 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1286 int32_t visible_tracks = 0;
1287 while (visible_tracks < y_span ) {
1289 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1290 /* passing through a hidden track */
1295 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1296 /* moving to a non-track; disallow */
1302 /* moving beyond the lowest visible track; disallow */
1306 } else if (y_span < 0) {
1308 /* moving down the canvas */
1309 if ((new_order - y_span) <= tavs.visible_y_high) {
1311 int32_t visible_tracks = 0;
1313 while (visible_tracks > y_span ) {
1316 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1317 /* passing through a hidden track */
1322 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1323 /* moving to a non-track; disallow */
1330 /* moving beyond the highest visible track; disallow */
1337 /* this is the pointer's track */
1339 if ((new_order - y_span) > tavs.visible_y_high) {
1340 /* we will overflow */
1342 } else if ((new_order - y_span) < tavs.visible_y_low) {
1343 /* we will overflow */
1352 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1353 : RegionMotionDrag (e, i, p, v, b),
1356 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1358 _dest_trackview = tv;
1359 if (tv->layer_display() == Overlaid) {
1362 _dest_layer = _primary->region()->layer ();
1366 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1367 if (rtv && rtv->is_track()) {
1368 speed = rtv->track()->speed ();
1371 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1375 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1377 RegionMotionDrag::start_grab (event, c);
1379 _pointer_frame_offset = grab_frame() - _last_frame_position;
1382 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1383 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1385 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1386 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1388 _primary = v->view()->create_region_view (r, false, false);
1390 _primary->get_canvas_group()->show ();
1391 _primary->set_position (pos, 0);
1392 _views.push_back (DraggingView (_primary));
1394 _last_frame_position = pos;
1396 _item = _primary->get_canvas_group ();
1397 _dest_trackview = v;
1398 _dest_layer = _primary->region()->layer ();
1401 map<RegionView*, pair<RouteTimeAxisView*, int> >
1402 RegionMotionDrag::find_time_axis_views_and_layers ()
1404 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1406 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1408 double ix1, ix2, iy1, iy2;
1409 RegionView* rv = i->view;
1410 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1411 rv->get_canvas_frame()->i2w (ix1, iy1);
1412 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1414 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1415 tav[rv] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1423 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1425 _editor->update_canvas_now ();
1427 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1429 RouteTimeAxisView* dest_rtv = final[_primary].first;
1431 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1432 _primary->get_canvas_group()->property_y() = 0;
1434 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1436 _editor->begin_reversible_command (_("insert region"));
1437 playlist->clear_history ();
1438 playlist->add_region (_primary->region (), _last_frame_position);
1439 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1440 _editor->commit_reversible_command ();
1448 RegionInsertDrag::aborted ()
1455 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1456 : RegionMoveDrag (e, i, p, v, false, false)
1461 struct RegionSelectionByPosition {
1462 bool operator() (RegionView*a, RegionView* b) {
1463 return a->region()->position () < b->region()->position();
1468 RegionSpliceDrag::motion (GdkEvent* event, bool)
1470 RouteTimeAxisView* tv;
1473 if (!check_possible (&tv, &layer)) {
1479 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1485 RegionSelection copy (_editor->selection->regions);
1487 RegionSelectionByPosition cmp;
1490 nframes64_t const pf = adjusted_current_frame (event);
1492 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1494 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1500 boost::shared_ptr<Playlist> playlist;
1502 if ((playlist = atv->playlist()) == 0) {
1506 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1511 if (pf < (*i)->region()->last_frame() + 1) {
1515 if (pf > (*i)->region()->first_frame()) {
1521 playlist->shuffle ((*i)->region(), dir);
1526 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1532 RegionSpliceDrag::aborted ()
1537 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1545 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1547 _dest_trackview = _view;
1549 Drag::start_grab (event);
1554 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1557 // TODO: create region-create-drag region view here
1560 // TODO: resize region-create-drag region view here
1564 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1566 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1572 if (!movement_occurred) {
1573 mtv->add_region (grab_frame ());
1575 motion (event, false);
1576 // TODO: create region-create-drag region here
1581 RegionCreateDrag::aborted ()
1586 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1594 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1597 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1599 Drag::start_grab (event);
1601 region = &cnote->region_view();
1603 double region_start = region->get_position_pixels();
1604 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1606 if (grab_x() <= middle_point) {
1607 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1610 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1614 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1616 if (event->motion.state & Keyboard::PrimaryModifier) {
1622 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1624 if (ms.size() > 1) {
1625 /* has to be relative, may make no sense otherwise */
1629 region->note_selected (cnote, true);
1631 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1632 MidiRegionSelection::iterator next;
1635 (*r)->begin_resizing (at_front);
1641 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1643 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1644 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1645 (*r)->update_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1650 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1652 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1653 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1654 (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1659 NoteResizeDrag::aborted ()
1665 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1671 RegionGainDrag::finished (GdkEvent *, bool)
1677 RegionGainDrag::aborted ()
1682 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1683 : RegionDrag (e, i, p, v)
1684 , _have_transaction (false)
1690 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1693 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1694 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1696 if (tv && tv->is_track()) {
1697 speed = tv->track()->speed();
1700 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1701 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1702 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1704 Drag::start_grab (event, _editor->trimmer_cursor);
1706 nframes64_t const pf = adjusted_current_frame (event);
1708 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1709 _operation = ContentsTrim;
1711 /* These will get overridden for a point trim.*/
1712 if (pf < (region_start + region_length/2)) {
1713 /* closer to start */
1714 _operation = StartTrim;
1715 } else if (pf > (region_end - region_length/2)) {
1717 _operation = EndTrim;
1721 switch (_operation) {
1723 _editor->show_verbose_time_cursor (region_start, 10);
1726 _editor->show_verbose_time_cursor (region_end, 10);
1729 _editor->show_verbose_time_cursor (pf, 10);
1735 TrimDrag::motion (GdkEvent* event, bool first_move)
1737 RegionView* rv = _primary;
1739 /* snap modifier works differently here..
1740 its current state has to be passed to the
1741 various trim functions in order to work properly
1745 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1746 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1747 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1749 if (tv && tv->is_track()) {
1750 speed = tv->track()->speed();
1753 nframes64_t const pf = adjusted_current_frame (event);
1759 switch (_operation) {
1761 trim_type = "Region start trim";
1764 trim_type = "Region end trim";
1767 trim_type = "Region content trim";
1771 _editor->begin_reversible_command (trim_type);
1772 _have_transaction = true;
1774 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1775 RegionView* rv = i->view;
1776 rv->fake_set_opaque(false);
1777 rv->region()->clear_history ();
1778 rv->region()->suspend_property_changes ();
1780 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1783 arv->temporarily_hide_envelope ();
1786 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1787 insert_result = _editor->motion_frozen_playlists.insert (pl);
1789 if (insert_result.second) {
1795 bool non_overlap_trim = false;
1797 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1798 non_overlap_trim = true;
1801 switch (_operation) {
1803 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1804 _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1809 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1810 _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1816 bool swap_direction = false;
1818 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1819 swap_direction = true;
1822 nframes64_t frame_delta = 0;
1824 bool left_direction = false;
1825 if (last_pointer_frame() > pf) {
1826 left_direction = true;
1829 if (left_direction) {
1830 frame_delta = (last_pointer_frame() - pf);
1832 frame_delta = (pf - last_pointer_frame());
1835 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1836 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1842 switch (_operation) {
1844 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1847 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1850 _editor->show_verbose_time_cursor (pf, 10);
1857 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1859 if (movement_occurred) {
1860 motion (event, false);
1862 if (!_editor->selection->selected (_primary)) {
1863 _editor->thaw_region_after_trim (*_primary);
1866 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1867 _editor->thaw_region_after_trim (*i->view);
1868 i->view->fake_set_opaque (true);
1869 if (_have_transaction) {
1870 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1874 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1878 _editor->motion_frozen_playlists.clear ();
1880 if (_have_transaction) {
1881 _editor->commit_reversible_command();
1885 /* no mouse movement */
1886 _editor->point_trim (event, adjusted_current_frame (event));
1891 TrimDrag::aborted ()
1893 /* Our motion method is changing model state, so use the Undo system
1894 to cancel. Perhaps not ideal, as this will leave an Undo point
1895 behind which may be slightly odd from the user's point of view.
1900 if (_have_transaction) {
1905 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1909 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1914 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1917 // create a dummy marker for visual representation of moving the copy.
1918 // The actual copying is not done before we reach the finish callback.
1920 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1921 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1922 *new MeterSection (_marker->meter()));
1924 _item = &new_marker->the_item ();
1925 _marker = new_marker;
1929 MetricSection& section (_marker->meter());
1931 if (!section.movable()) {
1937 Drag::start_grab (event, cursor);
1939 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1941 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1945 MeterMarkerDrag::motion (GdkEvent* event, bool)
1947 nframes64_t const pf = adjusted_current_frame (event);
1949 _marker->set_position (pf);
1951 _editor->show_verbose_time_cursor (pf, 10);
1955 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1957 if (!movement_occurred) {
1961 motion (event, false);
1965 TempoMap& map (_editor->session()->tempo_map());
1966 map.bbt_time (last_pointer_frame(), when);
1968 if (_copy == true) {
1969 _editor->begin_reversible_command (_("copy meter mark"));
1970 XMLNode &before = map.get_state();
1971 map.add_meter (_marker->meter(), when);
1972 XMLNode &after = map.get_state();
1973 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1974 _editor->commit_reversible_command ();
1976 // delete the dummy marker we used for visual representation of copying.
1977 // a new visual marker will show up automatically.
1980 _editor->begin_reversible_command (_("move meter mark"));
1981 XMLNode &before = map.get_state();
1982 map.move_meter (_marker->meter(), when);
1983 XMLNode &after = map.get_state();
1984 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1985 _editor->commit_reversible_command ();
1990 MeterMarkerDrag::aborted ()
1992 _marker->set_position (_marker->meter().frame ());
1995 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1999 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2004 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2009 // create a dummy marker for visual representation of moving the copy.
2010 // The actual copying is not done before we reach the finish callback.
2012 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2013 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2014 *new TempoSection (_marker->tempo()));
2016 _item = &new_marker->the_item ();
2017 _marker = new_marker;
2021 MetricSection& section (_marker->tempo());
2023 if (!section.movable()) {
2028 Drag::start_grab (event, cursor);
2030 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2031 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2035 TempoMarkerDrag::motion (GdkEvent* event, bool)
2037 nframes64_t const pf = adjusted_current_frame (event);
2038 _marker->set_position (pf);
2039 _editor->show_verbose_time_cursor (pf, 10);
2043 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2045 if (!movement_occurred) {
2049 motion (event, false);
2053 TempoMap& map (_editor->session()->tempo_map());
2054 map.bbt_time (last_pointer_frame(), when);
2056 if (_copy == true) {
2057 _editor->begin_reversible_command (_("copy tempo mark"));
2058 XMLNode &before = map.get_state();
2059 map.add_tempo (_marker->tempo(), when);
2060 XMLNode &after = map.get_state();
2061 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2062 _editor->commit_reversible_command ();
2064 // delete the dummy marker we used for visual representation of copying.
2065 // a new visual marker will show up automatically.
2068 _editor->begin_reversible_command (_("move tempo mark"));
2069 XMLNode &before = map.get_state();
2070 map.move_tempo (_marker->tempo(), when);
2071 XMLNode &after = map.get_state();
2072 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2073 _editor->commit_reversible_command ();
2078 TempoMarkerDrag::aborted ()
2080 _marker->set_position (_marker->tempo().frame());
2083 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2087 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2092 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2094 Drag::start_grab (event, c);
2098 nframes64_t where = _editor->event_frame (event, 0, 0);
2100 _editor->snap_to_with_modifier (where, event);
2101 _editor->playhead_cursor->set_position (where);
2105 if (_cursor == _editor->playhead_cursor) {
2106 _editor->_dragging_playhead = true;
2108 if (_editor->session() && _was_rolling && _stop) {
2109 _editor->session()->request_stop ();
2112 if (_editor->session() && _editor->session()->is_auditioning()) {
2113 _editor->session()->cancel_audition ();
2117 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2119 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2123 CursorDrag::motion (GdkEvent* event, bool)
2125 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2127 if (adjusted_frame == last_pointer_frame()) {
2131 _cursor->set_position (adjusted_frame);
2133 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2136 _editor->update_canvas_now ();
2138 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2142 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2144 _editor->_dragging_playhead = false;
2146 if (!movement_occurred && _stop) {
2150 motion (event, false);
2152 if (_item == &_editor->playhead_cursor->canvas_item) {
2153 if (_editor->session()) {
2154 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2155 _editor->_pending_locate_request = true;
2161 CursorDrag::aborted ()
2163 _editor->_dragging_playhead = false;
2164 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2167 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2168 : RegionDrag (e, i, p, v)
2174 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2176 Drag::start_grab (event, cursor);
2178 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2179 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2181 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2182 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2186 FadeInDrag::motion (GdkEvent* event, bool)
2188 nframes64_t fade_length;
2190 nframes64_t const pos = adjusted_current_frame (event);
2192 boost::shared_ptr<Region> region = _primary->region ();
2194 if (pos < (region->position() + 64)) {
2195 fade_length = 64; // this should be a minimum defined somewhere
2196 } else if (pos > region->last_frame()) {
2197 fade_length = region->length();
2199 fade_length = pos - region->position();
2202 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2204 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2210 tmp->reset_fade_in_shape_width (fade_length);
2213 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2217 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2219 if (!movement_occurred) {
2223 nframes64_t fade_length;
2225 nframes64_t const pos = adjusted_current_frame (event);
2227 boost::shared_ptr<Region> region = _primary->region ();
2229 if (pos < (region->position() + 64)) {
2230 fade_length = 64; // this should be a minimum defined somewhere
2231 } else if (pos > region->last_frame()) {
2232 fade_length = region->length();
2234 fade_length = pos - region->position();
2237 _editor->begin_reversible_command (_("change fade in length"));
2239 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2241 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2247 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2248 XMLNode &before = alist->get_state();
2250 tmp->audio_region()->set_fade_in_length (fade_length);
2251 tmp->audio_region()->set_fade_in_active (true);
2253 XMLNode &after = alist->get_state();
2254 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2257 _editor->commit_reversible_command ();
2261 FadeInDrag::aborted ()
2263 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2264 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2270 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2274 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2275 : RegionDrag (e, i, p, v)
2281 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2283 Drag::start_grab (event, cursor);
2285 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2286 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2288 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2289 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2293 FadeOutDrag::motion (GdkEvent* event, bool)
2295 nframes64_t fade_length;
2297 nframes64_t const pos = adjusted_current_frame (event);
2299 boost::shared_ptr<Region> region = _primary->region ();
2301 if (pos > (region->last_frame() - 64)) {
2302 fade_length = 64; // this should really be a minimum fade defined somewhere
2304 else if (pos < region->position()) {
2305 fade_length = region->length();
2308 fade_length = region->last_frame() - pos;
2311 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2313 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2319 tmp->reset_fade_out_shape_width (fade_length);
2322 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2326 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2328 if (!movement_occurred) {
2332 nframes64_t fade_length;
2334 nframes64_t const pos = adjusted_current_frame (event);
2336 boost::shared_ptr<Region> region = _primary->region ();
2338 if (pos > (region->last_frame() - 64)) {
2339 fade_length = 64; // this should really be a minimum fade defined somewhere
2341 else if (pos < region->position()) {
2342 fade_length = region->length();
2345 fade_length = region->last_frame() - pos;
2348 _editor->begin_reversible_command (_("change fade out length"));
2350 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2352 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2358 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2359 XMLNode &before = alist->get_state();
2361 tmp->audio_region()->set_fade_out_length (fade_length);
2362 tmp->audio_region()->set_fade_out_active (true);
2364 XMLNode &after = alist->get_state();
2365 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2368 _editor->commit_reversible_command ();
2372 FadeOutDrag::aborted ()
2374 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2375 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2381 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2385 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2388 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2391 _points.push_back (Gnome::Art::Point (0, 0));
2392 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2394 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2395 _line->property_width_pixels() = 1;
2396 _line->property_points () = _points;
2399 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2402 MarkerDrag::~MarkerDrag ()
2404 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2410 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2412 Drag::start_grab (event, cursor);
2416 Location *location = _editor->find_location_from_marker (_marker, is_start);
2417 _editor->_dragging_edit_point = true;
2419 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2421 update_item (location);
2423 // _drag_line->show();
2424 // _line->raise_to_top();
2427 _editor->show_verbose_time_cursor (location->start(), 10);
2429 _editor->show_verbose_time_cursor (location->end(), 10);
2432 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2435 case Selection::Toggle:
2436 _editor->selection->toggle (_marker);
2438 case Selection::Set:
2439 if (!_editor->selection->selected (_marker)) {
2440 _editor->selection->set (_marker);
2443 case Selection::Extend:
2445 Locations::LocationList ll;
2446 list<Marker*> to_add;
2448 _editor->selection->markers.range (s, e);
2449 s = min (_marker->position(), s);
2450 e = max (_marker->position(), e);
2453 if (e < max_frames) {
2456 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2457 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2458 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2461 to_add.push_back (lm->start);
2464 to_add.push_back (lm->end);
2468 if (!to_add.empty()) {
2469 _editor->selection->add (to_add);
2473 case Selection::Add:
2474 _editor->selection->add (_marker);
2478 /* Set up copies for us to manipulate during the drag */
2480 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2481 Location* l = _editor->find_location_from_marker (*i, is_start);
2482 _copied_locations.push_back (new Location (*l));
2487 MarkerDrag::motion (GdkEvent* event, bool)
2489 nframes64_t f_delta = 0;
2491 bool move_both = false;
2493 Location *real_location;
2494 Location *copy_location = 0;
2496 nframes64_t const newframe = adjusted_current_frame (event);
2498 nframes64_t next = newframe;
2500 if (newframe == last_pointer_frame()) {
2504 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2508 MarkerSelection::iterator i;
2509 list<Location*>::iterator x;
2511 /* find the marker we're dragging, and compute the delta */
2513 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2514 x != _copied_locations.end() && i != _editor->selection->markers.end();
2520 if (marker == _marker) {
2522 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2527 if (real_location->is_mark()) {
2528 f_delta = newframe - copy_location->start();
2532 switch (marker->type()) {
2534 case Marker::LoopStart:
2535 case Marker::PunchIn:
2536 f_delta = newframe - copy_location->start();
2540 case Marker::LoopEnd:
2541 case Marker::PunchOut:
2542 f_delta = newframe - copy_location->end();
2545 /* what kind of marker is this ? */
2553 if (i == _editor->selection->markers.end()) {
2554 /* hmm, impossible - we didn't find the dragged marker */
2558 /* now move them all */
2560 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2561 x != _copied_locations.end() && i != _editor->selection->markers.end();
2567 /* call this to find out if its the start or end */
2569 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2573 if (real_location->locked()) {
2577 if (copy_location->is_mark()) {
2581 copy_location->set_start (copy_location->start() + f_delta);
2585 nframes64_t new_start = copy_location->start() + f_delta;
2586 nframes64_t new_end = copy_location->end() + f_delta;
2588 if (is_start) { // start-of-range marker
2591 copy_location->set_start (new_start);
2592 copy_location->set_end (new_end);
2593 } else if (new_start < copy_location->end()) {
2594 copy_location->set_start (new_start);
2596 _editor->snap_to (next, 1, true);
2597 copy_location->set_end (next);
2598 copy_location->set_start (newframe);
2601 } else { // end marker
2604 copy_location->set_end (new_end);
2605 copy_location->set_start (new_start);
2606 } else if (new_end > copy_location->start()) {
2607 copy_location->set_end (new_end);
2608 } else if (newframe > 0) {
2609 _editor->snap_to (next, -1, true);
2610 copy_location->set_start (next);
2611 copy_location->set_end (newframe);
2616 update_item (copy_location);
2618 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2621 lm->set_position (copy_location->start(), copy_location->end());
2625 assert (!_copied_locations.empty());
2627 _editor->show_verbose_time_cursor (newframe, 10);
2630 _editor->update_canvas_now ();
2635 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2637 if (!movement_occurred) {
2639 /* just a click, do nothing but finish
2640 off the selection process
2643 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2646 case Selection::Set:
2647 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2648 _editor->selection->set (_marker);
2652 case Selection::Toggle:
2653 case Selection::Extend:
2654 case Selection::Add:
2661 _editor->_dragging_edit_point = false;
2663 _editor->begin_reversible_command ( _("move marker") );
2664 XMLNode &before = _editor->session()->locations()->get_state();
2666 MarkerSelection::iterator i;
2667 list<Location*>::iterator x;
2670 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2671 x != _copied_locations.end() && i != _editor->selection->markers.end();
2674 Location * location = _editor->find_location_from_marker (*i, is_start);
2678 if (location->locked()) {
2682 if (location->is_mark()) {
2683 location->set_start ((*x)->start());
2685 location->set ((*x)->start(), (*x)->end());
2690 XMLNode &after = _editor->session()->locations()->get_state();
2691 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2692 _editor->commit_reversible_command ();
2698 MarkerDrag::aborted ()
2704 MarkerDrag::update_item (Location* location)
2706 double const x1 = _editor->frame_to_pixel (location->start());
2708 _points.front().set_x(x1);
2709 _points.back().set_x(x1);
2710 _line->property_points() = _points;
2713 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2715 _cumulative_x_drag (0),
2716 _cumulative_y_drag (0)
2718 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2724 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2726 Drag::start_grab (event, _editor->fader_cursor);
2728 // start the grab at the center of the control point so
2729 // the point doesn't 'jump' to the mouse after the first drag
2730 _time_axis_view_grab_x = _point->get_x();
2731 _time_axis_view_grab_y = _point->get_y();
2733 float const fraction = 1 - (_point->get_y() / _point->line().height());
2735 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2737 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2738 event->button.x + 10, event->button.y + 10);
2740 _editor->show_verbose_canvas_cursor ();
2744 ControlPointDrag::motion (GdkEvent* event, bool)
2746 double dx = _drags->current_pointer_x() - last_pointer_x();
2747 double dy = _drags->current_pointer_y() - last_pointer_y();
2749 if (event->button.state & Keyboard::SecondaryModifier) {
2754 /* coordinate in TimeAxisView's space */
2755 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2756 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2758 // calculate zero crossing point. back off by .01 to stay on the
2759 // positive side of zero
2760 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2762 // make sure we hit zero when passing through
2763 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2767 if (_x_constrained) {
2768 cx = _time_axis_view_grab_x;
2770 if (_y_constrained) {
2771 cy = _time_axis_view_grab_y;
2774 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2775 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2779 cy = min ((double) _point->line().height(), cy);
2781 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2783 if (!_x_constrained) {
2784 _editor->snap_to_with_modifier (cx_frames, event);
2787 float const fraction = 1.0 - (cy / _point->line().height());
2789 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2791 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2793 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2797 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2799 if (!movement_occurred) {
2803 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2804 _editor->reset_point_selection ();
2808 motion (event, false);
2810 _point->line().end_drag ();
2814 ControlPointDrag::aborted ()
2816 _point->line().reset ();
2820 ControlPointDrag::active (Editing::MouseMode m)
2822 if (m == Editing::MouseGain) {
2823 /* always active in mouse gain */
2827 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2828 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2831 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2834 _cumulative_y_drag (0)
2839 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2841 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2844 _item = &_line->grab_item ();
2846 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2847 origin, and ditto for y.
2850 double cx = event->button.x;
2851 double cy = event->button.y;
2853 _line->parent_group().w2i (cx, cy);
2855 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2860 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2861 /* no adjacent points */
2865 Drag::start_grab (event, _editor->fader_cursor);
2867 /* store grab start in parent frame */
2869 _time_axis_view_grab_x = cx;
2870 _time_axis_view_grab_y = cy;
2872 double fraction = 1.0 - (cy / _line->height());
2874 _line->start_drag_line (before, after, fraction);
2876 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2877 event->button.x + 10, event->button.y + 10);
2879 _editor->show_verbose_canvas_cursor ();
2883 LineDrag::motion (GdkEvent* event, bool)
2885 double dy = _drags->current_pointer_y() - last_pointer_y();
2887 if (event->button.state & Keyboard::SecondaryModifier) {
2891 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2893 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2896 cy = min ((double) _line->height(), cy);
2898 double const fraction = 1.0 - (cy / _line->height());
2902 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2908 /* we are ignoring x position for this drag, so we can just pass in anything */
2909 _line->drag_motion (0, fraction, true, push);
2911 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2915 LineDrag::finished (GdkEvent* event, bool)
2917 motion (event, false);
2922 LineDrag::aborted ()
2928 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2930 Drag::start_grab (event);
2931 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2935 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2942 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2944 nframes64_t grab = grab_frame ();
2945 if (Config->get_rubberbanding_snaps_to_grid ()) {
2946 _editor->snap_to_with_modifier (grab, event);
2949 /* base start and end on initial click position */
2959 if (_drags->current_pointer_y() < grab_y()) {
2960 y1 = _drags->current_pointer_y();
2963 y2 = _drags->current_pointer_y();
2968 if (start != end || y1 != y2) {
2970 double x1 = _editor->frame_to_pixel (start);
2971 double x2 = _editor->frame_to_pixel (end);
2973 _editor->rubberband_rect->property_x1() = x1;
2974 _editor->rubberband_rect->property_y1() = y1;
2975 _editor->rubberband_rect->property_x2() = x2;
2976 _editor->rubberband_rect->property_y2() = y2;
2978 _editor->rubberband_rect->show();
2979 _editor->rubberband_rect->raise_to_top();
2981 _editor->show_verbose_time_cursor (pf, 10);
2986 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2988 if (movement_occurred) {
2990 motion (event, false);
2993 if (_drags->current_pointer_y() < grab_y()) {
2994 y1 = _drags->current_pointer_y();
2997 y2 = _drags->current_pointer_y();
3002 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3005 _editor->begin_reversible_command (_("rubberband selection"));
3007 if (grab_frame() < last_pointer_frame()) {
3008 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3010 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3014 _editor->commit_reversible_command ();
3018 if (!getenv("ARDOUR_SAE")) {
3019 _editor->selection->clear_tracks();
3021 _editor->selection->clear_regions();
3022 _editor->selection->clear_points ();
3023 _editor->selection->clear_lines ();
3026 _editor->rubberband_rect->hide();
3030 RubberbandSelectDrag::aborted ()
3032 _editor->rubberband_rect->hide ();
3036 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3038 Drag::start_grab (event);
3040 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3044 TimeFXDrag::motion (GdkEvent* event, bool)
3046 RegionView* rv = _primary;
3048 nframes64_t const pf = adjusted_current_frame (event);
3050 if (pf > rv->region()->position()) {
3051 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3054 _editor->show_verbose_time_cursor (pf, 10);
3058 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3060 _primary->get_time_axis_view().hide_timestretch ();
3062 if (!movement_occurred) {
3066 if (last_pointer_frame() < _primary->region()->position()) {
3067 /* backwards drag of the left edge - not usable */
3071 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3073 float percentage = (double) newlen / (double) _primary->region()->length();
3075 #ifndef USE_RUBBERBAND
3076 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3077 if (_primary->region()->data_type() == DataType::AUDIO) {
3078 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3082 _editor->begin_reversible_command (_("timestretch"));
3084 // XXX how do timeFX on multiple regions ?
3089 if (_editor->time_stretch (rs, percentage) == -1) {
3090 error << _("An error occurred while executing time stretch operation") << endmsg;
3095 TimeFXDrag::aborted ()
3097 _primary->get_time_axis_view().hide_timestretch ();
3102 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3104 Drag::start_grab (event);
3108 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3110 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3114 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3116 if (movement_occurred && _editor->session()) {
3117 /* make sure we stop */
3118 _editor->session()->request_transport_speed (0.0);
3123 ScrubDrag::aborted ()
3128 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3132 , _original_pointer_time_axis (-1)
3133 , _last_pointer_time_axis (-1)
3139 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3141 nframes64_t start = 0;
3142 nframes64_t end = 0;
3144 if (_editor->session() == 0) {
3148 Gdk::Cursor* cursor = 0;
3150 switch (_operation) {
3151 case CreateSelection:
3152 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3157 cursor = _editor->selector_cursor;
3158 Drag::start_grab (event, cursor);
3161 case SelectionStartTrim:
3162 if (_editor->clicked_axisview) {
3163 _editor->clicked_axisview->order_selection_trims (_item, true);
3165 Drag::start_grab (event, _editor->trimmer_cursor);
3166 start = _editor->selection->time[_editor->clicked_selection].start;
3167 _pointer_frame_offset = grab_frame() - start;
3170 case SelectionEndTrim:
3171 if (_editor->clicked_axisview) {
3172 _editor->clicked_axisview->order_selection_trims (_item, false);
3174 Drag::start_grab (event, _editor->trimmer_cursor);
3175 end = _editor->selection->time[_editor->clicked_selection].end;
3176 _pointer_frame_offset = grab_frame() - end;
3180 start = _editor->selection->time[_editor->clicked_selection].start;
3181 Drag::start_grab (event, cursor);
3182 _pointer_frame_offset = grab_frame() - start;
3186 if (_operation == SelectionMove) {
3187 _editor->show_verbose_time_cursor (start, 10);
3189 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3192 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3196 SelectionDrag::motion (GdkEvent* event, bool first_move)
3198 nframes64_t start = 0;
3199 nframes64_t end = 0;
3202 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3203 if (pending_time_axis.first == 0) {
3207 nframes64_t const pending_position = adjusted_current_frame (event);
3209 /* only alter selection if things have changed */
3211 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3215 switch (_operation) {
3216 case CreateSelection:
3218 nframes64_t grab = grab_frame ();
3221 _editor->snap_to (grab);
3224 if (pending_position < grab_frame()) {
3225 start = pending_position;
3228 end = pending_position;
3232 /* first drag: Either add to the selection
3233 or create a new selection
3239 /* adding to the selection */
3240 _editor->selection->add (_editor->clicked_axisview);
3241 _editor->clicked_selection = _editor->selection->add (start, end);
3246 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3247 _editor->selection->set (_editor->clicked_axisview);
3250 _editor->clicked_selection = _editor->selection->set (start, end);
3254 /* select the track that we're in */
3255 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3256 _editor->selection->add (pending_time_axis.first);
3257 _added_time_axes.push_back (pending_time_axis.first);
3260 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3261 tracks that we selected in the first place.
3264 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3265 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3267 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3268 while (i != _added_time_axes.end()) {
3270 list<TimeAxisView*>::iterator tmp = i;
3273 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3274 _editor->selection->remove (*i);
3275 _added_time_axes.remove (*i);
3284 case SelectionStartTrim:
3286 start = _editor->selection->time[_editor->clicked_selection].start;
3287 end = _editor->selection->time[_editor->clicked_selection].end;
3289 if (pending_position > end) {
3292 start = pending_position;
3296 case SelectionEndTrim:
3298 start = _editor->selection->time[_editor->clicked_selection].start;
3299 end = _editor->selection->time[_editor->clicked_selection].end;
3301 if (pending_position < start) {
3304 end = pending_position;
3311 start = _editor->selection->time[_editor->clicked_selection].start;
3312 end = _editor->selection->time[_editor->clicked_selection].end;
3314 length = end - start;
3316 start = pending_position;
3317 _editor->snap_to (start);
3319 end = start + length;
3324 if (event->button.x >= _editor->_horizontal_position + _editor->_canvas_width) {
3325 _editor->start_canvas_autoscroll (1, 0);
3329 _editor->selection->replace (_editor->clicked_selection, start, end);
3332 if (_operation == SelectionMove) {
3333 _editor->show_verbose_time_cursor(start, 10);
3335 _editor->show_verbose_time_cursor(pending_position, 10);
3340 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3342 Session* s = _editor->session();
3344 if (movement_occurred) {
3345 motion (event, false);
3346 /* XXX this is not object-oriented programming at all. ick */
3347 if (_editor->selection->time.consolidate()) {
3348 _editor->selection->TimeChanged ();
3351 /* XXX what if its a music time selection? */
3352 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3353 s->request_play_range (&_editor->selection->time, true);
3358 /* just a click, no pointer movement.*/
3360 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3361 _editor->selection->clear_time();
3364 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3365 _editor->selection->set (_editor->clicked_axisview);
3368 if (s && s->get_play_range () && s->transport_rolling()) {
3369 s->request_stop (false, false);
3374 _editor->stop_canvas_autoscroll ();
3378 SelectionDrag::aborted ()
3383 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3388 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3389 _drag_rect->hide ();
3391 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3392 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3396 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3398 if (_editor->session() == 0) {
3402 Gdk::Cursor* cursor = 0;
3404 if (!_editor->temp_location) {
3405 _editor->temp_location = new Location;
3408 switch (_operation) {
3409 case CreateRangeMarker:
3410 case CreateTransportMarker:
3411 case CreateCDMarker:
3413 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3418 cursor = _editor->selector_cursor;
3422 Drag::start_grab (event, cursor);
3424 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3428 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3430 nframes64_t start = 0;
3431 nframes64_t end = 0;
3432 ArdourCanvas::SimpleRect *crect;
3434 switch (_operation) {
3435 case CreateRangeMarker:
3436 crect = _editor->range_bar_drag_rect;
3438 case CreateTransportMarker:
3439 crect = _editor->transport_bar_drag_rect;
3441 case CreateCDMarker:
3442 crect = _editor->cd_marker_bar_drag_rect;
3445 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3450 nframes64_t const pf = adjusted_current_frame (event);
3452 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3453 nframes64_t grab = grab_frame ();
3454 _editor->snap_to (grab);
3456 if (pf < grab_frame()) {
3464 /* first drag: Either add to the selection
3465 or create a new selection.
3470 _editor->temp_location->set (start, end);
3474 update_item (_editor->temp_location);
3476 //_drag_rect->raise_to_top();
3481 if (event->button.x >= _editor->_horizontal_position + _editor->_canvas_width) {
3482 _editor->start_canvas_autoscroll (1, 0);
3486 _editor->temp_location->set (start, end);
3488 double x1 = _editor->frame_to_pixel (start);
3489 double x2 = _editor->frame_to_pixel (end);
3490 crect->property_x1() = x1;
3491 crect->property_x2() = x2;
3493 update_item (_editor->temp_location);
3496 _editor->show_verbose_time_cursor (pf, 10);
3501 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3503 Location * newloc = 0;
3507 if (movement_occurred) {
3508 motion (event, false);
3511 switch (_operation) {
3512 case CreateRangeMarker:
3513 case CreateCDMarker:
3515 _editor->begin_reversible_command (_("new range marker"));
3516 XMLNode &before = _editor->session()->locations()->get_state();
3517 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3518 if (_operation == CreateCDMarker) {
3519 flags = Location::IsRangeMarker | Location::IsCDMarker;
3520 _editor->cd_marker_bar_drag_rect->hide();
3523 flags = Location::IsRangeMarker;
3524 _editor->range_bar_drag_rect->hide();
3526 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3527 _editor->session()->locations()->add (newloc, true);
3528 XMLNode &after = _editor->session()->locations()->get_state();
3529 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3530 _editor->commit_reversible_command ();
3534 case CreateTransportMarker:
3535 // popup menu to pick loop or punch
3536 _editor->new_transport_marker_context_menu (&event->button, _item);
3540 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3542 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3547 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3549 if (end == max_frames) {
3550 end = _editor->session()->current_end_frame ();
3553 if (start == max_frames) {
3554 start = _editor->session()->current_start_frame ();
3557 switch (_editor->mouse_mode) {
3559 /* find the two markers on either side and then make the selection from it */
3560 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3564 /* find the two markers on either side of the click and make the range out of it */
3565 _editor->selection->set (start, end);
3574 _editor->stop_canvas_autoscroll ();
3578 RangeMarkerBarDrag::aborted ()
3584 RangeMarkerBarDrag::update_item (Location* location)
3586 double const x1 = _editor->frame_to_pixel (location->start());
3587 double const x2 = _editor->frame_to_pixel (location->end());
3589 _drag_rect->property_x1() = x1;
3590 _drag_rect->property_x2() = x2;
3594 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3596 Drag::start_grab (event, _editor->zoom_cursor);
3597 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3601 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3606 nframes64_t const pf = adjusted_current_frame (event);
3608 nframes64_t grab = grab_frame ();
3609 _editor->snap_to_with_modifier (grab, event);
3611 /* base start and end on initial click position */
3623 _editor->zoom_rect->show();
3624 _editor->zoom_rect->raise_to_top();
3627 _editor->reposition_zoom_rect(start, end);
3629 _editor->show_verbose_time_cursor (pf, 10);
3634 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3636 if (movement_occurred) {
3637 motion (event, false);
3639 if (grab_frame() < last_pointer_frame()) {
3640 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3642 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3645 _editor->temporal_zoom_to_frame (false, grab_frame());
3647 temporal_zoom_step (false);
3648 center_screen (grab_frame());
3652 _editor->zoom_rect->hide();
3656 MouseZoomDrag::aborted ()
3658 _editor->zoom_rect->hide ();
3661 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3664 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3665 region = &cnote->region_view();
3669 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3671 Drag::start_grab (event);
3674 drag_delta_note = 0;
3679 event_x = _drags->current_pointer_x();
3680 event_y = _drags->current_pointer_y();
3682 _item->property_parent().get_value()->w2i(event_x, event_y);
3684 last_x = region->snap_to_pixel(event_x);
3687 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3689 if (!(was_selected = cnote->selected())) {
3691 /* tertiary-click means extend selection - we'll do that on button release,
3692 so don't add it here, because otherwise we make it hard to figure
3693 out the "extend-to" range.
3696 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3699 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3702 region->note_selected (cnote, true);
3704 region->unique_select (cnote);
3711 NoteDrag::motion (GdkEvent*, bool)
3713 MidiStreamView* streamview = region->midi_stream_view();
3717 event_x = _drags->current_pointer_x();
3718 event_y = _drags->current_pointer_y();
3720 _item->property_parent().get_value()->w2i(event_x, event_y);
3722 event_x = region->snap_to_pixel(event_x);
3724 double dx = event_x - last_x;
3725 double dy = event_y - last_y;
3730 // Snap to note rows
3732 if (abs (dy) < streamview->note_height()) {
3735 int8_t this_delta_note;
3737 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3739 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3741 drag_delta_note -= this_delta_note;
3742 dy = streamview->note_height() * this_delta_note;
3743 last_y = last_y + dy;
3747 region->move_selection (dx, dy);
3749 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3751 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3752 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3753 _editor->show_verbose_canvas_cursor_with (buf);
3758 NoteDrag::finished (GdkEvent* ev, bool moved)
3760 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3763 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3766 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3768 region->note_deselected (cnote);
3771 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3772 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3774 if (!extend && !add && region->selection_size() > 1) {
3775 region->unique_select(cnote);
3776 } else if (extend) {
3777 region->note_selected (cnote, true, true);
3779 /* it was added during button press */
3784 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3789 NoteDrag::aborted ()
3794 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3797 , _nothing_to_drag (false)
3799 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3802 _line = _atav->line ();
3806 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3808 Drag::start_grab (event, cursor);
3810 list<ControlPoint*> points;
3812 XMLNode* state = &_line->get_state ();
3814 if (_ranges.empty()) {
3816 uint32_t const N = _line->npoints ();
3817 for (uint32_t i = 0; i < N; ++i) {
3818 points.push_back (_line->nth (i));
3823 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3824 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3826 /* fade into and out of the region that we're dragging;
3827 64 samples length plucked out of thin air.
3829 nframes64_t const h = (j->start + j->end) / 2;
3830 nframes64_t a = j->start + 64;
3834 nframes64_t b = j->end - 64;
3839 the_list->add (j->start, the_list->eval (j->start));
3840 _line->add_always_in_view (j->start);
3841 the_list->add (a, the_list->eval (a));
3842 _line->add_always_in_view (a);
3843 the_list->add (b, the_list->eval (b));
3844 _line->add_always_in_view (b);
3845 the_list->add (j->end, the_list->eval (j->end));
3846 _line->add_always_in_view (j->end);
3849 uint32_t const N = _line->npoints ();
3850 for (uint32_t i = 0; i < N; ++i) {
3852 ControlPoint* p = _line->nth (i);
3854 list<AudioRange>::const_iterator j = _ranges.begin ();
3855 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3859 if (j != _ranges.end()) {
3860 points.push_back (p);
3865 if (points.empty()) {
3866 _nothing_to_drag = true;
3870 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3874 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3876 if (_nothing_to_drag) {
3880 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3882 /* we are ignoring x position for this drag, so we can just pass in anything */
3883 _line->drag_motion (0, f, true, false);
3887 AutomationRangeDrag::finished (GdkEvent* event, bool)
3889 if (_nothing_to_drag) {
3893 motion (event, false);
3895 _line->clear_always_in_view ();
3899 AutomationRangeDrag::aborted ()
3901 _line->clear_always_in_view ();
3905 DraggingView::DraggingView (RegionView* v)
3908 initial_y = v->get_canvas_group()->property_y ();