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/diskstream.h"
26 #include "ardour/session.h"
27 #include "ardour/dB.h"
28 #include "ardour/region_factory.h"
29 #include "ardour/midi_diskstream.h"
33 #include "audio_region_view.h"
34 #include "midi_region_view.h"
35 #include "ardour_ui.h"
36 #include "gui_thread.h"
37 #include "control_point.h"
39 #include "region_gain_line.h"
40 #include "editor_drag.h"
41 #include "audio_time_axis.h"
42 #include "midi_time_axis.h"
43 #include "canvas-note.h"
44 #include "selection.h"
45 #include "midi_selection.h"
46 #include "automation_time_axis.h"
49 using namespace ARDOUR;
52 using namespace Editing;
53 using namespace ArdourCanvas;
55 using Gtkmm2ext::Keyboard;
57 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
59 DragManager::DragManager (Editor* e)
62 , _current_pointer_frame (0)
67 DragManager::~DragManager ()
75 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
84 DragManager::break_drag ()
88 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
99 DragManager::add (Drag* d)
101 d->set_manager (this);
102 _drags.push_back (d);
106 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
108 assert (_drags.empty ());
109 d->set_manager (this);
110 _drags.push_back (d);
115 DragManager::start_grab (GdkEvent* e)
117 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
119 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
120 (*i)->start_grab (e);
125 DragManager::end_grab (GdkEvent* e)
130 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
131 bool const t = (*i)->end_grab (e);
146 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
150 _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
152 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
153 bool const t = (*i)->motion_handler (e, from_autoscroll);
164 DragManager::have_item (ArdourCanvas::Item* i) const
166 list<Drag*>::const_iterator j = _drags.begin ();
167 while (j != _drags.end() && (*j)->item () != i) {
171 return j != _drags.end ();
174 Drag::Drag (Editor* e, ArdourCanvas::Item* i)
177 , _pointer_frame_offset (0)
178 , _move_threshold_passed (false)
180 , _last_pointer_frame (0)
186 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
192 cursor = _editor->which_grabber_cursor ();
195 _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
199 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
202 cursor = _editor->which_grabber_cursor ();
205 // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
207 if (Keyboard::is_button2_event (&event->button)) {
208 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
209 _y_constrained = true;
210 _x_constrained = false;
212 _y_constrained = false;
213 _x_constrained = true;
216 _x_constrained = false;
217 _y_constrained = false;
220 _grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
221 _grab_frame = adjusted_frame (_grab_frame, event);
222 _last_pointer_frame = _grab_frame;
223 _last_pointer_x = _grab_x;
224 _last_pointer_y = _grab_y;
226 _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
230 if (_editor->session() && _editor->session()->transport_rolling()) {
233 _was_rolling = false;
236 switch (_editor->snap_type()) {
237 case SnapToRegionStart:
238 case SnapToRegionEnd:
239 case SnapToRegionSync:
240 case SnapToRegionBoundary:
241 _editor->build_region_boundary_cache ();
248 /** @param event GDK event, or 0.
249 * @return true if some movement occurred, otherwise false.
252 Drag::end_grab (GdkEvent* event)
254 _editor->stop_canvas_autoscroll ();
256 _item->ungrab (event ? event->button.time : 0);
258 finished (event, _move_threshold_passed);
260 _editor->hide_verbose_canvas_cursor();
262 return _move_threshold_passed;
266 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
270 if (f > _pointer_frame_offset) {
271 pos = f - _pointer_frame_offset;
275 _editor->snap_to_with_modifier (pos, event);
282 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
284 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
288 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
290 /* check to see if we have moved in any way that matters since the last motion event */
291 if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
292 (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
296 pair<nframes64_t, int> const threshold = move_threshold ();
298 bool const old_move_threshold_passed = _move_threshold_passed;
300 if (!from_autoscroll && !_move_threshold_passed) {
302 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
303 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
305 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
308 if (active (_editor->mouse_mode) && _move_threshold_passed) {
310 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
311 if (!from_autoscroll) {
312 _editor->maybe_autoscroll (&event->motion, allow_vertical_autoscroll ());
315 motion (event, _move_threshold_passed != old_move_threshold_passed);
317 _last_pointer_x = _drags->current_pointer_x ();
318 _last_pointer_y = _drags->current_pointer_y ();
319 _last_pointer_frame = adjusted_current_frame (event);
337 _editor->stop_canvas_autoscroll ();
338 _editor->hide_verbose_canvas_cursor ();
341 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
346 RegionView::RegionViewGoingAway.connect (death_connection, ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
350 RegionDrag::region_going_away (RegionView* v)
355 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
356 : RegionDrag (e, i, p, v),
367 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
369 Drag::start_grab (event);
371 _editor->show_verbose_time_cursor (_last_frame_position, 10);
374 RegionMotionDrag::TimeAxisViewSummary
375 RegionMotionDrag::get_time_axis_view_summary ()
377 int32_t children = 0;
378 TimeAxisViewSummary sum;
380 _editor->visible_order_range (&sum.visible_y_low, &sum.visible_y_high);
382 /* get a bitmask representing the visible tracks */
384 for (TrackViewList::iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
385 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
386 TimeAxisView::Children children_list;
388 /* zeroes are audio/MIDI tracks. ones are other types. */
390 if (!rtv->hidden()) {
392 if (!rtv->is_track()) {
393 /* not an audio nor MIDI track */
394 sum.tracks = sum.tracks |= (0x01 << rtv->order());
397 sum.height_list[rtv->order()] = (*i)->current_height();
400 if ((children_list = rtv->get_child_list()).size() > 0) {
401 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
402 sum.tracks = sum.tracks |= (0x01 << (rtv->order() + children));
403 sum.height_list[rtv->order() + children] = (*j)->current_height();
414 RegionMotionDrag::compute_y_delta (
415 TimeAxisView const * last_pointer_view, TimeAxisView* current_pointer_view,
416 int32_t last_pointer_layer, int32_t current_pointer_layer,
417 TimeAxisViewSummary const & tavs,
418 int32_t* pointer_order_span, int32_t* pointer_layer_span,
419 int32_t* canvas_pointer_order_span
423 *pointer_order_span = 0;
424 *pointer_layer_span = 0;
428 bool clamp_y_axis = false;
430 /* the change in track order between this callback and the last */
431 *pointer_order_span = last_pointer_view->order() - current_pointer_view->order();
432 /* the change in layer between this callback and the last;
433 only meaningful if pointer_order_span == 0 (ie we've not moved tracks) */
434 *pointer_layer_span = last_pointer_layer - current_pointer_layer;
436 if (*pointer_order_span != 0) {
438 /* find the actual pointer span, in terms of the number of visible tracks;
439 to do this, we reduce |pointer_order_span| by the number of hidden tracks
442 *canvas_pointer_order_span = *pointer_order_span;
443 if (last_pointer_view->order() >= current_pointer_view->order()) {
444 for (int32_t y = current_pointer_view->order(); y < last_pointer_view->order(); y++) {
445 if (tavs.height_list[y] == 0) {
446 *canvas_pointer_order_span--;
450 for (int32_t y = last_pointer_view->order(); y <= current_pointer_view->order(); y++) {
451 if (tavs.height_list[y] == 0) {
452 *canvas_pointer_order_span++;
457 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
459 RegionView* rv = (*i);
461 if (rv->region()->locked()) {
465 double ix1, ix2, iy1, iy2;
466 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
467 rv->get_canvas_frame()->i2w (ix1, iy1);
468 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
470 /* get the new trackview for this particular region */
471 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (iy1);
473 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
475 /* XXX: not sure that we should be passing canvas_pointer_order_span in here,
476 as surely this is a per-region thing... */
478 clamp_y_axis = y_movement_disallowed (
479 rtv->order(), last_pointer_view->order(), *canvas_pointer_order_span, tavs
487 } else if (_dest_trackview == current_pointer_view) {
489 if (current_pointer_layer == last_pointer_layer) {
490 /* No movement; clamp */
496 _dest_trackview = current_pointer_view;
497 _dest_layer = current_pointer_layer;
505 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
507 /* compute the amount of pointer motion in frames, and where
508 the region would be if we moved it by that much.
510 *pending_region_position = adjusted_current_frame (event);
512 nframes64_t sync_frame;
513 nframes64_t sync_offset;
516 sync_offset = _primary->region()->sync_offset (sync_dir);
518 /* we don't handle a sync point that lies before zero.
520 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
522 sync_frame = *pending_region_position + (sync_dir*sync_offset);
524 _editor->snap_to_with_modifier (sync_frame, event);
526 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
529 *pending_region_position = _last_frame_position;
532 if (*pending_region_position > max_frames - _primary->region()->length()) {
533 *pending_region_position = _last_frame_position;
538 if ((*pending_region_position != _last_frame_position) && x_move_allowed ()) {
540 /* now compute the canvas unit distance we need to move the regionview
541 to make it appear at the new location.
544 x_delta = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
546 if (*pending_region_position <= _last_frame_position) {
548 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
550 RegionView* rv = (*i);
552 // If any regionview is at zero, we need to know so we can stop further leftward motion.
554 double ix1, ix2, iy1, iy2;
555 rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
556 rv->get_canvas_frame()->i2w (ix1, iy1);
558 if (-x_delta > ix1 + _editor->horizontal_adjustment.get_value()) {
560 *pending_region_position = _last_frame_position;
567 _last_frame_position = *pending_region_position;
574 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
578 TimeAxisViewSummary tavs = get_time_axis_view_summary ();
580 vector<int32_t>::iterator j;
582 /* *pointer* variables reflect things about the pointer; as we may be moving
583 multiple regions, much detail must be computed per-region */
585 /* current_pointer_view will become the TimeAxisView that we're currently pointing at, and
586 current_pointer_layer the current layer on that TimeAxisView; in this code layer numbers
587 are with respect to how the view's layers are displayed; if we are in Overlaid mode, layer
588 is always 0 regardless of what the region's "real" layer is */
589 RouteTimeAxisView* current_pointer_view;
590 layer_t current_pointer_layer;
591 if (!check_possible (¤t_pointer_view, ¤t_pointer_layer)) {
595 /* TimeAxisView that we were pointing at last time we entered this method */
596 TimeAxisView const * const last_pointer_view = _dest_trackview;
597 /* the order of the track that we were pointing at last time we entered this method */
598 int32_t const last_pointer_order = last_pointer_view->order ();
599 /* the layer that we were pointing at last time we entered this method */
600 layer_t const last_pointer_layer = _dest_layer;
602 int32_t pointer_order_span;
603 int32_t pointer_layer_span;
604 int32_t canvas_pointer_order_span;
606 bool const clamp_y_axis = compute_y_delta (
607 last_pointer_view, current_pointer_view,
608 last_pointer_layer, current_pointer_layer, tavs,
609 &pointer_order_span, &pointer_layer_span,
610 &canvas_pointer_order_span
613 nframes64_t pending_region_position;
614 double const x_delta = compute_x_delta (event, &pending_region_position);
616 /*************************************************************
618 ************************************************************/
620 if (x_delta == 0 && pointer_order_span == 0 && pointer_layer_span == 0 && !first_move) {
621 /* haven't reached next snap point, and we're not switching
622 trackviews nor layers. nothing to do.
627 /*************************************************************
629 ************************************************************/
631 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
633 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
635 RegionView* rv = (*i);
637 if (rv->region()->locked()) {
641 /* here we are calculating the y distance from the
642 top of the first track view to the top of the region
643 area of the track view that we're working on */
645 /* this x value is just a dummy value so that we have something
650 /* distance from the top of this track view to the region area
651 of our track view is always 1 */
655 /* convert to world coordinates, ie distance from the top of
658 rv->get_canvas_frame()->i2w (ix1, iy1);
660 /* compensate for the ruler section and the vertical scrollbar position */
661 iy1 += _editor->get_trackview_group_vertical_offset ();
665 // hide any dependent views
667 rv->get_time_axis_view().hide_dependent_views (*rv);
670 reparent to a non scrolling group so that we can keep the
671 region selection above all time axis views.
672 reparenting means we have to move the rv as the two
673 parent groups have different coordinates.
676 rv->get_canvas_group()->property_y() = iy1 - 1;
677 rv->get_canvas_group()->reparent(*(_editor->_region_motion_group));
679 rv->fake_set_opaque (true);
682 /* current view for this particular region */
683 pair<TimeAxisView*, int> pos = _editor->trackview_by_y_position (iy1);
684 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
686 if (pointer_order_span != 0 && !clamp_y_axis) {
688 /* INTER-TRACK MOVEMENT */
690 /* move through the height list to the track that the region is currently on */
691 vector<int32_t>::iterator j = tavs.height_list.begin ();
693 while (j != tavs.height_list.end () && x != rtv->order ()) {
699 int32_t temp_pointer_order_span = canvas_pointer_order_span;
701 if (j != tavs.height_list.end ()) {
703 /* Account for layers in the original and
704 destination tracks. If we're moving around in layers we assume
705 that only one track is involved, so it's ok to use *pointer*
708 StreamView* lv = last_pointer_view->view ();
711 /* move to the top of the last trackview */
712 if (lv->layer_display () == Stacked) {
713 y_delta -= (lv->layers() - last_pointer_layer - 1) * lv->child_height ();
716 StreamView* cv = current_pointer_view->view ();
719 /* move to the right layer on the current trackview */
720 if (cv->layer_display () == Stacked) {
721 y_delta += (cv->layers() - current_pointer_layer - 1) * cv->child_height ();
724 /* And for being on a non-topmost layer on the new
727 while (temp_pointer_order_span > 0) {
728 /* we're moving up canvas-wise,
729 so we need to find the next track height
731 if (j != tavs.height_list.begin()) {
735 if (x != last_pointer_order) {
737 ++temp_pointer_order_span;
742 temp_pointer_order_span--;
745 while (temp_pointer_order_span < 0) {
749 if (x != last_pointer_order) {
751 --temp_pointer_order_span;
755 if (j != tavs.height_list.end()) {
759 temp_pointer_order_span++;
763 /* find out where we'll be when we move and set height accordingly */
765 pair<TimeAxisView*, int> const pos = _editor->trackview_by_y_position (iy1 + y_delta);
766 RouteTimeAxisView const * temp_rtv = dynamic_cast<RouteTimeAxisView*> (pos.first);
767 rv->set_height (temp_rtv->view()->child_height());
769 /* if you un-comment the following, the region colours will follow
770 the track colours whilst dragging; personally
771 i think this can confuse things, but never mind.
774 //const GdkColor& col (temp_rtv->view->get_region_color());
775 //rv->set_color (const_cast<GdkColor&>(col));
779 if (pointer_order_span == 0 && pointer_layer_span != 0 && !clamp_y_axis) {
781 /* INTER-LAYER MOVEMENT in the same track */
782 y_delta = rtv->view()->child_height () * pointer_layer_span;
787 _editor->mouse_brush_insert_region (rv, pending_region_position);
789 rv->move (x_delta, y_delta);
792 } /* foreach region */
794 _total_x_delta += x_delta;
797 _editor->cursor_group->raise_to_top();
800 if (x_delta != 0 && !_brushing) {
801 _editor->show_verbose_time_cursor (_last_frame_position, 10);
806 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
808 if (_copy && first_move) {
809 copy_regions (event);
812 RegionMotionDrag::motion (event, first_move);
816 RegionMoveDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
818 vector<RegionView*> copies;
819 boost::shared_ptr<Diskstream> ds;
820 boost::shared_ptr<Playlist> from_playlist;
821 boost::shared_ptr<Playlist> to_playlist;
822 RegionSelection new_views;
823 typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
824 PlaylistSet modified_playlists;
825 PlaylistSet frozen_playlists;
826 list <sigc::connection> modified_playlist_connections;
827 pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
828 nframes64_t drag_delta;
829 bool changed_tracks, changed_position;
830 map<RegionView*, pair<RouteTimeAxisView*, int> > final;
831 RouteTimeAxisView* source_tv;
832 vector<StatefulDiffCommand*> sdc;
834 if (!movement_occurred) {
840 /* all changes were made during motion event handlers */
843 for (list<RegionView*>::iterator i = _views.begin(); i != _views.end(); ++i) {
844 copies.push_back (*i);
851 /* reverse this here so that we have the correct logic to finalize
855 if (Config->get_edit_mode() == Lock) {
856 _x_constrained = !_x_constrained;
860 if (_x_constrained) {
861 _editor->begin_reversible_command (_("fixed time region copy"));
863 _editor->begin_reversible_command (_("region copy"));
866 if (_x_constrained) {
867 _editor->begin_reversible_command (_("fixed time region drag"));
869 _editor->begin_reversible_command (_("region drag"));
873 changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
874 changed_tracks = (_dest_trackview != &_primary->get_time_axis_view());
876 drag_delta = _primary->region()->position() - _last_frame_position;
878 _editor->update_canvas_now ();
880 /* make a list of where each region ended up */
881 final = find_time_axis_views_and_layers ();
883 cerr << "Iterate over " << _views.size() << " views\n";
885 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ) {
887 RegionView* rv = (*i);
888 RouteTimeAxisView* dest_rtv = final[*i].first;
889 layer_t dest_layer = final[*i].second;
893 from_playlist.reset ();
894 to_playlist.reset ();
896 if (rv->region()->locked()) {
901 if (changed_position && !_x_constrained) {
902 where = rv->region()->position() - drag_delta;
904 where = rv->region()->position();
907 boost::shared_ptr<Region> new_region;
910 /* we already made a copy */
911 new_region = rv->region();
913 /* undo the previous hide_dependent_views so that xfades don't
914 disappear on copying regions
917 //rv->get_time_axis_view().reveal_dependent_views (*rv);
919 } else if (changed_tracks && dest_rtv->playlist()) {
920 new_region = RegionFactory::create (rv->region());
923 if (changed_tracks || _copy) {
925 to_playlist = dest_rtv->playlist();
932 _editor->latest_regionviews.clear ();
934 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*_editor, &Editor::collect_new_region_view));
936 insert_result = modified_playlists.insert (to_playlist);
938 if (insert_result.second) {
939 to_playlist->clear_history ();
942 cerr << "To playlist " << to_playlist->name() << " region history contains "
943 << to_playlist->region_list().change().added.size() << " adds and "
944 << to_playlist->region_list().change().removed.size() << " removes\n";
946 cerr << "Adding new region " << new_region->id() << " based on "
947 << rv->region()->id() << endl;
949 to_playlist->add_region (new_region, where);
951 if (dest_rtv->view()->layer_display() == Stacked) {
952 new_region->set_layer (dest_layer);
953 new_region->set_pending_explicit_relayer (true);
958 if (!_editor->latest_regionviews.empty()) {
959 // XXX why just the first one ? we only expect one
960 // commented out in nick_m's canvas reworking. is that intended?
961 //dest_atv->reveal_dependent_views (*latest_regionviews.front());
962 new_views.push_back (_editor->latest_regionviews.front());
966 rv->region()->clear_history ();
969 motion on the same track. plonk the previously reparented region
970 back to its original canvas group (its streamview).
971 No need to do anything for copies as they are fake regions which will be deleted.
974 rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
975 rv->get_canvas_group()->property_y() = 0;
976 rv->get_time_axis_view().reveal_dependent_views (*rv);
978 /* just change the model */
980 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
982 if (dest_rtv->view()->layer_display() == Stacked) {
983 rv->region()->set_layer (dest_layer);
984 rv->region()->set_pending_explicit_relayer (true);
987 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
989 frozen_insert_result = frozen_playlists.insert(playlist);
991 if (frozen_insert_result.second) {
995 cerr << "Moving region " << rv->region()->id() << endl;
997 rv->region()->set_position (where, (void*) this);
999 sdc.push_back (new StatefulDiffCommand (rv->region()));
1002 if (changed_tracks && !_copy) {
1004 /* get the playlist where this drag started. we can't use rv->region()->playlist()
1005 because we may have copied the region and it has not been attached to a playlist.
1008 source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
1009 ds = source_tv->get_diskstream();
1010 from_playlist = ds->playlist();
1014 assert (from_playlist);
1016 /* moved to a different audio track, without copying */
1018 /* the region that used to be in the old playlist is not
1019 moved to the new one - we use a copy of it. as a result,
1020 any existing editor for the region should no longer be
1024 rv->hide_region_editor();
1025 rv->fake_set_opaque (false);
1027 /* remove the region from the old playlist */
1029 insert_result = modified_playlists.insert (from_playlist);
1031 if (insert_result.second) {
1032 from_playlist->clear_history ();
1035 cerr << "From playlist " << from_playlist->name() << " region history contains "
1036 << from_playlist->region_list().change().added.size() << " adds and "
1037 << from_playlist->region_list().change().removed.size() << " removes\n";
1039 cerr << "removing region " << rv->region() << endl;
1041 from_playlist->remove_region (rv->region());
1043 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1044 was selected in all of them, then removing it from a playlist will have removed all
1045 trace of it from the selection (i.e. there were N regions selected, we removed 1,
1046 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1047 corresponding regionview, and the selection is now empty).
1049 this could have invalidated any and all iterators into the region selection.
1051 the heuristic we use here is: if the region selection is empty, break out of the loop
1052 here. if the region selection is not empty, then restart the loop because we know that
1053 we must have removed at least the region(view) we've just been working on as well as any
1054 that we processed on previous iterations.
1056 EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
1057 we can just iterate.
1060 if (_views.empty()) {
1062 sdc.push_back (new StatefulDiffCommand (to_playlist));
1063 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1066 if (from_playlist && (from_playlist != to_playlist)) {
1067 sdc.push_back (new StatefulDiffCommand (from_playlist));
1068 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1080 copies.push_back (rv);
1083 cerr << "Done with TV, top = " << to_playlist << " from = " << from_playlist << endl;
1086 sdc.push_back (new StatefulDiffCommand (to_playlist));
1087 cerr << "Saved diff for to:" << to_playlist->name() << endl;
1090 if (from_playlist && (from_playlist != to_playlist)) {
1091 sdc.push_back (new StatefulDiffCommand (from_playlist));
1092 cerr << "Saved diff for from:" << from_playlist->name() << endl;
1097 if we've created new regions either by copying or moving
1098 to a new track, we want to replace the old selection with the new ones
1101 if (new_views.size() > 0) {
1102 _editor->selection->set (new_views);
1105 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1110 for (vector<StatefulDiffCommand*>::iterator i = sdc.begin(); i != sdc.end(); ++i) {
1111 _editor->session()->add_command (*i);
1114 _editor->commit_reversible_command ();
1116 for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1122 RegionMoveDrag::aborted ()
1126 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1133 RegionMotionDrag::aborted ();
1138 RegionMotionDrag::aborted ()
1140 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1141 TimeAxisView* tv = &(*i)->get_time_axis_view ();
1142 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1144 (*i)->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1145 (*i)->get_canvas_group()->property_y() = 0;
1146 (*i)->get_time_axis_view().reveal_dependent_views (**i);
1147 (*i)->fake_set_opaque (false);
1148 (*i)->move (-_total_x_delta, 0);
1149 (*i)->set_height (rtv->view()->child_height ());
1152 _editor->update_canvas_now ();
1157 RegionMotionDrag::x_move_allowed () const
1159 if (Config->get_edit_mode() == Lock) {
1160 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1161 return _x_constrained;
1164 return !_x_constrained;
1168 RegionMotionDrag::copy_regions (GdkEvent* event)
1170 /* duplicate the regionview(s) and region(s) */
1172 list<RegionView*> new_regionviews;
1174 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1176 RegionView* rv = (*i);
1177 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1178 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1180 const boost::shared_ptr<const Region> original = rv->region();
1181 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1185 boost::shared_ptr<AudioRegion> audioregion_copy
1186 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1187 nrv = new AudioRegionView (*arv, audioregion_copy);
1189 boost::shared_ptr<MidiRegion> midiregion_copy
1190 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1191 nrv = new MidiRegionView (*mrv, midiregion_copy);
1196 nrv->get_canvas_group()->show ();
1197 new_regionviews.push_back (nrv);
1199 /* swap _primary to the copy */
1201 if (rv == _primary) {
1205 /* ..and deselect the one we copied */
1207 rv->set_selected (false);
1210 if (new_regionviews.empty()) {
1214 /* reflect the fact that we are dragging the copies */
1216 _views = new_regionviews;
1218 swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1221 sync the canvas to what we think is its current state
1222 without it, the canvas seems to
1223 "forget" to update properly after the upcoming reparent()
1224 ..only if the mouse is in rapid motion at the time of the grab.
1225 something to do with regionview creation taking so long?
1227 _editor->update_canvas_now();
1231 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1233 /* Which trackview is this ? */
1235 pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1236 (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1237 (*layer) = tvp.second;
1239 if (*tv && (*tv)->layer_display() == Overlaid) {
1243 /* The region motion is only processed if the pointer is over
1247 if (!(*tv) || !(*tv)->is_track()) {
1248 /* To make sure we hide the verbose canvas cursor when the mouse is
1249 not held over and audiotrack.
1251 _editor->hide_verbose_canvas_cursor ();
1258 /** @param new_order New track order.
1259 * @param old_order Old track order.
1260 * @param visible_y_low Lowest visible order.
1261 * @return true if y movement should not happen, otherwise false.
1264 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1266 if (new_order != old_order) {
1268 /* this isn't the pointer track */
1272 /* moving up the canvas */
1273 if ( (new_order - y_span) >= tavs.visible_y_low) {
1277 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1278 int32_t visible_tracks = 0;
1279 while (visible_tracks < y_span ) {
1281 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1282 /* passing through a hidden track */
1287 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1288 /* moving to a non-track; disallow */
1294 /* moving beyond the lowest visible track; disallow */
1298 } else if (y_span < 0) {
1300 /* moving down the canvas */
1301 if ((new_order - y_span) <= tavs.visible_y_high) {
1303 int32_t visible_tracks = 0;
1305 while (visible_tracks > y_span ) {
1308 while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1309 /* passing through a hidden track */
1314 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1315 /* moving to a non-track; disallow */
1322 /* moving beyond the highest visible track; disallow */
1329 /* this is the pointer's track */
1331 if ((new_order - y_span) > tavs.visible_y_high) {
1332 /* we will overflow */
1334 } else if ((new_order - y_span) < tavs.visible_y_low) {
1335 /* we will overflow */
1344 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1345 : RegionMotionDrag (e, i, p, v, b),
1348 TimeAxisView* const tv = &_primary->get_time_axis_view ();
1350 _dest_trackview = tv;
1351 if (tv->layer_display() == Overlaid) {
1354 _dest_layer = _primary->region()->layer ();
1358 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1359 if (rtv && rtv->is_track()) {
1360 speed = rtv->get_diskstream()->speed ();
1363 _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1367 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1369 RegionMotionDrag::start_grab (event, c);
1371 _pointer_frame_offset = grab_frame() - _last_frame_position;
1374 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1375 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1377 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1378 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1380 _primary = v->view()->create_region_view (r, false, false);
1382 _primary->get_canvas_group()->show ();
1383 _primary->set_position (pos, 0);
1384 _views.push_back (_primary);
1386 _last_frame_position = pos;
1388 _item = _primary->get_canvas_group ();
1389 _dest_trackview = v;
1390 _dest_layer = _primary->region()->layer ();
1393 map<RegionView*, pair<RouteTimeAxisView*, int> >
1394 RegionMotionDrag::find_time_axis_views_and_layers ()
1396 map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1398 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1400 double ix1, ix2, iy1, iy2;
1401 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1402 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1403 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1405 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1406 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1414 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1416 _editor->update_canvas_now ();
1418 map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1420 RouteTimeAxisView* dest_rtv = final[_primary].first;
1422 _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1423 _primary->get_canvas_group()->property_y() = 0;
1425 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1427 _editor->begin_reversible_command (_("insert region"));
1428 playlist->clear_history ();
1429 playlist->add_region (_primary->region (), _last_frame_position);
1430 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1431 _editor->commit_reversible_command ();
1439 RegionInsertDrag::aborted ()
1444 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1445 : RegionMoveDrag (e, i, p, v, false, false)
1450 struct RegionSelectionByPosition {
1451 bool operator() (RegionView*a, RegionView* b) {
1452 return a->region()->position () < b->region()->position();
1457 RegionSpliceDrag::motion (GdkEvent* event, bool)
1459 RouteTimeAxisView* tv;
1462 if (!check_possible (&tv, &layer)) {
1468 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1474 RegionSelection copy (_editor->selection->regions);
1476 RegionSelectionByPosition cmp;
1479 nframes64_t const pf = adjusted_current_frame (event);
1481 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1483 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1489 boost::shared_ptr<Playlist> playlist;
1491 if ((playlist = atv->playlist()) == 0) {
1495 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1500 if (pf < (*i)->region()->last_frame() + 1) {
1504 if (pf > (*i)->region()->first_frame()) {
1510 playlist->shuffle ((*i)->region(), dir);
1515 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1521 RegionSpliceDrag::aborted ()
1526 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1534 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1536 _dest_trackview = _view;
1538 Drag::start_grab (event);
1543 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1546 // TODO: create region-create-drag region view here
1549 // TODO: resize region-create-drag region view here
1553 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1555 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1561 if (!movement_occurred) {
1562 mtv->add_region (grab_frame ());
1564 motion (event, false);
1565 // TODO: create region-create-drag region here
1570 RegionCreateDrag::aborted ()
1575 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1583 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1586 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1588 Drag::start_grab (event);
1590 region = &cnote->region_view();
1592 double region_start = region->get_position_pixels();
1593 double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1595 if (grab_x() <= middle_point) {
1596 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1599 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1603 _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1605 if (event->motion.state & Keyboard::PrimaryModifier) {
1611 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1613 if (ms.size() > 1) {
1614 /* has to be relative, may make no sense otherwise */
1618 region->note_selected (cnote, true);
1620 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1621 MidiRegionSelection::iterator next;
1624 (*r)->begin_resizing (at_front);
1630 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1632 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1633 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1634 (*r)->update_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1639 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1641 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1642 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1643 (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1648 NoteResizeDrag::aborted ()
1654 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1660 RegionGainDrag::finished (GdkEvent *, bool)
1666 RegionGainDrag::aborted ()
1671 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1672 : RegionDrag (e, i, p, v)
1673 , _have_transaction (false)
1679 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1682 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1683 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1685 if (tv && tv->is_track()) {
1686 speed = tv->get_diskstream()->speed();
1689 nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1690 nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1691 nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1693 Drag::start_grab (event, _editor->trimmer_cursor);
1695 nframes64_t const pf = adjusted_current_frame (event);
1697 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1698 _operation = ContentsTrim;
1700 /* These will get overridden for a point trim.*/
1701 if (pf < (region_start + region_length/2)) {
1702 /* closer to start */
1703 _operation = StartTrim;
1704 } else if (pf > (region_end - region_length/2)) {
1706 _operation = EndTrim;
1710 switch (_operation) {
1712 _editor->show_verbose_time_cursor (region_start, 10);
1715 _editor->show_verbose_time_cursor (region_end, 10);
1718 _editor->show_verbose_time_cursor (pf, 10);
1724 TrimDrag::motion (GdkEvent* event, bool first_move)
1726 RegionView* rv = _primary;
1727 nframes64_t frame_delta = 0;
1729 bool left_direction;
1730 bool obey_snap = event ? !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) : false;
1732 /* snap modifier works differently here..
1733 its current state has to be passed to the
1734 various trim functions in order to work properly
1738 TimeAxisView* tvp = &_primary->get_time_axis_view ();
1739 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1740 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1742 if (tv && tv->is_track()) {
1743 speed = tv->get_diskstream()->speed();
1746 nframes64_t const pf = adjusted_current_frame (event);
1748 if (last_pointer_frame() > pf) {
1749 left_direction = true;
1751 left_direction = false;
1758 switch (_operation) {
1760 trim_type = "Region start trim";
1763 trim_type = "Region end trim";
1766 trim_type = "Region content trim";
1770 _editor->begin_reversible_command (trim_type);
1771 _have_transaction = true;
1773 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1774 (*i)->fake_set_opaque(false);
1775 (*i)->region()->clear_history ();
1776 (*i)->region()->suspend_property_changes ();
1778 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1781 arv->temporarily_hide_envelope ();
1784 boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1785 insert_result = _editor->motion_frozen_playlists.insert (pl);
1787 if (insert_result.second) {
1793 if (left_direction) {
1794 frame_delta = (last_pointer_frame() - pf);
1796 frame_delta = (pf - last_pointer_frame());
1799 bool non_overlap_trim = false;
1801 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1802 non_overlap_trim = true;
1805 switch (_operation) {
1807 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1811 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1812 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1818 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1822 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1823 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1830 bool swap_direction = false;
1832 if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1833 swap_direction = true;
1836 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1837 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1843 switch (_operation) {
1845 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1848 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1851 _editor->show_verbose_time_cursor (pf, 10);
1858 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1860 if (movement_occurred) {
1861 motion (event, false);
1863 if (!_editor->selection->selected (_primary)) {
1864 _editor->thaw_region_after_trim (*_primary);
1867 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1868 _editor->thaw_region_after_trim (**i);
1869 (*i)->fake_set_opaque (true);
1870 if (_have_transaction) {
1871 _editor->session()->add_command (new StatefulDiffCommand ((*i)->region()));
1875 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1879 _editor->motion_frozen_playlists.clear ();
1881 if (_have_transaction) {
1882 _editor->commit_reversible_command();
1886 /* no mouse movement */
1887 _editor->point_trim (event, adjusted_current_frame (event));
1892 TrimDrag::aborted ()
1894 /* Our motion method is changing model state, so use the Undo system
1895 to cancel. Perhaps not ideal, as this will leave an Undo point
1896 behind which may be slightly odd from the user's point of view.
1901 if (_have_transaction) {
1906 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1910 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1915 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1918 // create a dummy marker for visual representation of moving the copy.
1919 // The actual copying is not done before we reach the finish callback.
1921 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1922 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1923 *new MeterSection (_marker->meter()));
1925 _item = &new_marker->the_item ();
1926 _marker = new_marker;
1930 MetricSection& section (_marker->meter());
1932 if (!section.movable()) {
1938 Drag::start_grab (event, cursor);
1940 _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1942 _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1946 MeterMarkerDrag::motion (GdkEvent* event, bool)
1948 nframes64_t const pf = adjusted_current_frame (event);
1950 _marker->set_position (pf);
1952 _editor->show_verbose_time_cursor (pf, 10);
1956 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1958 if (!movement_occurred) {
1962 motion (event, false);
1966 TempoMap& map (_editor->session()->tempo_map());
1967 map.bbt_time (last_pointer_frame(), when);
1969 if (_copy == true) {
1970 _editor->begin_reversible_command (_("copy meter mark"));
1971 XMLNode &before = map.get_state();
1972 map.add_meter (_marker->meter(), when);
1973 XMLNode &after = map.get_state();
1974 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1975 _editor->commit_reversible_command ();
1977 // delete the dummy marker we used for visual representation of copying.
1978 // a new visual marker will show up automatically.
1981 _editor->begin_reversible_command (_("move meter mark"));
1982 XMLNode &before = map.get_state();
1983 map.move_meter (_marker->meter(), when);
1984 XMLNode &after = map.get_state();
1985 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1986 _editor->commit_reversible_command ();
1991 MeterMarkerDrag::aborted ()
1993 _marker->set_position (_marker->meter().frame ());
1996 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2000 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2005 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2010 // create a dummy marker for visual representation of moving the copy.
2011 // The actual copying is not done before we reach the finish callback.
2013 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2014 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2015 *new TempoSection (_marker->tempo()));
2017 _item = &new_marker->the_item ();
2018 _marker = new_marker;
2022 MetricSection& section (_marker->tempo());
2024 if (!section.movable()) {
2029 Drag::start_grab (event, cursor);
2031 _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2032 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2036 TempoMarkerDrag::motion (GdkEvent* event, bool)
2038 nframes64_t const pf = adjusted_current_frame (event);
2039 _marker->set_position (pf);
2040 _editor->show_verbose_time_cursor (pf, 10);
2044 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2046 if (!movement_occurred) {
2050 motion (event, false);
2054 TempoMap& map (_editor->session()->tempo_map());
2055 map.bbt_time (last_pointer_frame(), when);
2057 if (_copy == true) {
2058 _editor->begin_reversible_command (_("copy tempo mark"));
2059 XMLNode &before = map.get_state();
2060 map.add_tempo (_marker->tempo(), when);
2061 XMLNode &after = map.get_state();
2062 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2063 _editor->commit_reversible_command ();
2065 // delete the dummy marker we used for visual representation of copying.
2066 // a new visual marker will show up automatically.
2069 _editor->begin_reversible_command (_("move tempo mark"));
2070 XMLNode &before = map.get_state();
2071 map.move_tempo (_marker->tempo(), when);
2072 XMLNode &after = map.get_state();
2073 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2074 _editor->commit_reversible_command ();
2079 TempoMarkerDrag::aborted ()
2081 _marker->set_position (_marker->tempo().frame());
2084 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2088 _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2093 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2095 Drag::start_grab (event, c);
2099 nframes64_t where = _editor->event_frame (event, 0, 0);
2101 _editor->snap_to_with_modifier (where, event);
2102 _editor->playhead_cursor->set_position (where);
2106 if (_cursor == _editor->playhead_cursor) {
2107 _editor->_dragging_playhead = true;
2109 if (_editor->session() && _was_rolling && _stop) {
2110 _editor->session()->request_stop ();
2113 if (_editor->session() && _editor->session()->is_auditioning()) {
2114 _editor->session()->cancel_audition ();
2118 _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2120 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2124 CursorDrag::motion (GdkEvent* event, bool)
2126 nframes64_t const adjusted_frame = adjusted_current_frame (event);
2128 if (adjusted_frame == last_pointer_frame()) {
2132 _cursor->set_position (adjusted_frame);
2134 _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2137 _editor->update_canvas_now ();
2139 _editor->UpdateAllTransportClocks (_cursor->current_frame);
2143 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2145 _editor->_dragging_playhead = false;
2147 if (!movement_occurred && _stop) {
2151 motion (event, false);
2153 if (_item == &_editor->playhead_cursor->canvas_item) {
2154 if (_editor->session()) {
2155 _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2156 _editor->_pending_locate_request = true;
2162 CursorDrag::aborted ()
2164 _editor->_dragging_playhead = false;
2165 _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2168 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2169 : RegionDrag (e, i, p, v)
2175 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2177 Drag::start_grab (event, cursor);
2179 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2180 boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2182 _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2183 _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2187 FadeInDrag::motion (GdkEvent* event, bool)
2189 nframes64_t fade_length;
2191 nframes64_t const pos = adjusted_current_frame (event);
2193 boost::shared_ptr<Region> region = _primary->region ();
2195 if (pos < (region->position() + 64)) {
2196 fade_length = 64; // this should be a minimum defined somewhere
2197 } else if (pos > region->last_frame()) {
2198 fade_length = region->length();
2200 fade_length = pos - region->position();
2203 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2205 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2211 tmp->reset_fade_in_shape_width (fade_length);
2214 _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2218 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2220 if (!movement_occurred) {
2224 nframes64_t fade_length;
2226 nframes64_t const pos = adjusted_current_frame (event);
2228 boost::shared_ptr<Region> region = _primary->region ();
2230 if (pos < (region->position() + 64)) {
2231 fade_length = 64; // this should be a minimum defined somewhere
2232 } else if (pos > region->last_frame()) {
2233 fade_length = region->length();
2235 fade_length = pos - region->position();
2238 _editor->begin_reversible_command (_("change fade in length"));
2240 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2242 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2248 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2249 XMLNode &before = alist->get_state();
2251 tmp->audio_region()->set_fade_in_length (fade_length);
2252 tmp->audio_region()->set_fade_in_active (true);
2254 XMLNode &after = alist->get_state();
2255 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2258 _editor->commit_reversible_command ();
2262 FadeInDrag::aborted ()
2264 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2265 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2271 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2275 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2276 : RegionDrag (e, i, p, v)
2282 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2284 Drag::start_grab (event, cursor);
2286 AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2287 boost::shared_ptr<AudioRegion> r = a->audio_region ();
2289 _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2290 _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2294 FadeOutDrag::motion (GdkEvent* event, bool)
2296 nframes64_t fade_length;
2298 nframes64_t const pos = adjusted_current_frame (event);
2300 boost::shared_ptr<Region> region = _primary->region ();
2302 if (pos > (region->last_frame() - 64)) {
2303 fade_length = 64; // this should really be a minimum fade defined somewhere
2305 else if (pos < region->position()) {
2306 fade_length = region->length();
2309 fade_length = region->last_frame() - pos;
2312 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2314 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2320 tmp->reset_fade_out_shape_width (fade_length);
2323 _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2327 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2329 if (!movement_occurred) {
2333 nframes64_t fade_length;
2335 nframes64_t const pos = adjusted_current_frame (event);
2337 boost::shared_ptr<Region> region = _primary->region ();
2339 if (pos > (region->last_frame() - 64)) {
2340 fade_length = 64; // this should really be a minimum fade defined somewhere
2342 else if (pos < region->position()) {
2343 fade_length = region->length();
2346 fade_length = region->last_frame() - pos;
2349 _editor->begin_reversible_command (_("change fade out length"));
2351 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2353 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2359 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2360 XMLNode &before = alist->get_state();
2362 tmp->audio_region()->set_fade_out_length (fade_length);
2363 tmp->audio_region()->set_fade_out_active (true);
2365 XMLNode &after = alist->get_state();
2366 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2369 _editor->commit_reversible_command ();
2373 FadeOutDrag::aborted ()
2375 for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2376 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2382 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2386 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2389 _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2392 _points.push_back (Gnome::Art::Point (0, 0));
2393 _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2395 _line = new ArdourCanvas::Line (*_editor->timebar_group);
2396 _line->property_width_pixels() = 1;
2397 _line->property_points () = _points;
2400 _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2403 MarkerDrag::~MarkerDrag ()
2405 for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2411 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2413 Drag::start_grab (event, cursor);
2417 Location *location = _editor->find_location_from_marker (_marker, is_start);
2418 _editor->_dragging_edit_point = true;
2420 _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2422 update_item (location);
2424 // _drag_line->show();
2425 // _line->raise_to_top();
2428 _editor->show_verbose_time_cursor (location->start(), 10);
2430 _editor->show_verbose_time_cursor (location->end(), 10);
2433 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2436 case Selection::Toggle:
2437 _editor->selection->toggle (_marker);
2439 case Selection::Set:
2440 if (!_editor->selection->selected (_marker)) {
2441 _editor->selection->set (_marker);
2444 case Selection::Extend:
2446 Locations::LocationList ll;
2447 list<Marker*> to_add;
2449 _editor->selection->markers.range (s, e);
2450 s = min (_marker->position(), s);
2451 e = max (_marker->position(), e);
2454 if (e < max_frames) {
2457 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2458 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2459 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2462 to_add.push_back (lm->start);
2465 to_add.push_back (lm->end);
2469 if (!to_add.empty()) {
2470 _editor->selection->add (to_add);
2474 case Selection::Add:
2475 _editor->selection->add (_marker);
2479 /* set up copies for us to manipulate during the drag */
2481 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2482 Location *l = _editor->find_location_from_marker (*i, is_start);
2483 _copied_locations.push_back (new Location (*l));
2488 MarkerDrag::motion (GdkEvent* event, bool)
2490 nframes64_t f_delta = 0;
2492 bool move_both = false;
2494 Location *real_location;
2495 Location *copy_location = 0;
2497 nframes64_t const newframe = adjusted_current_frame (event);
2499 nframes64_t next = newframe;
2501 if (newframe == last_pointer_frame()) {
2505 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2509 MarkerSelection::iterator i;
2510 list<Location*>::iterator x;
2512 /* find the marker we're dragging, and compute the delta */
2514 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2515 x != _copied_locations.end() && i != _editor->selection->markers.end();
2521 if (marker == _marker) {
2523 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2528 if (real_location->is_mark()) {
2529 f_delta = newframe - copy_location->start();
2533 switch (marker->type()) {
2535 case Marker::LoopStart:
2536 case Marker::PunchIn:
2537 f_delta = newframe - copy_location->start();
2541 case Marker::LoopEnd:
2542 case Marker::PunchOut:
2543 f_delta = newframe - copy_location->end();
2546 /* what kind of marker is this ? */
2554 if (i == _editor->selection->markers.end()) {
2555 /* hmm, impossible - we didn't find the dragged marker */
2559 /* now move them all */
2561 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2562 x != _copied_locations.end() && i != _editor->selection->markers.end();
2568 /* call this to find out if its the start or end */
2570 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2574 if (real_location->locked()) {
2578 if (copy_location->is_mark()) {
2582 copy_location->set_start (copy_location->start() + f_delta);
2586 nframes64_t new_start = copy_location->start() + f_delta;
2587 nframes64_t new_end = copy_location->end() + f_delta;
2589 if (is_start) { // start-of-range marker
2592 copy_location->set_start (new_start);
2593 copy_location->set_end (new_end);
2594 } else if (new_start < copy_location->end()) {
2595 copy_location->set_start (new_start);
2597 _editor->snap_to (next, 1, true);
2598 copy_location->set_end (next);
2599 copy_location->set_start (newframe);
2602 } else { // end marker
2605 copy_location->set_end (new_end);
2606 copy_location->set_start (new_start);
2607 } else if (new_end > copy_location->start()) {
2608 copy_location->set_end (new_end);
2609 } else if (newframe > 0) {
2610 _editor->snap_to (next, -1, true);
2611 copy_location->set_start (next);
2612 copy_location->set_end (newframe);
2617 update_item (copy_location);
2619 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2622 lm->set_position (copy_location->start(), copy_location->end());
2626 assert (!_copied_locations.empty());
2628 _editor->show_verbose_time_cursor (newframe, 10);
2631 _editor->update_canvas_now ();
2636 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2638 if (!movement_occurred) {
2640 /* just a click, do nothing but finish
2641 off the selection process
2644 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2647 case Selection::Set:
2648 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2649 _editor->selection->set (_marker);
2653 case Selection::Toggle:
2654 case Selection::Extend:
2655 case Selection::Add:
2662 _editor->_dragging_edit_point = false;
2664 _editor->begin_reversible_command ( _("move marker") );
2665 XMLNode &before = _editor->session()->locations()->get_state();
2667 MarkerSelection::iterator i;
2668 list<Location*>::iterator x;
2671 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2672 x != _copied_locations.end() && i != _editor->selection->markers.end();
2675 Location * location = _editor->find_location_from_marker (*i, is_start);
2679 if (location->locked()) {
2683 if (location->is_mark()) {
2684 location->set_start ((*x)->start());
2686 location->set ((*x)->start(), (*x)->end());
2691 XMLNode &after = _editor->session()->locations()->get_state();
2692 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2693 _editor->commit_reversible_command ();
2699 MarkerDrag::aborted ()
2705 MarkerDrag::update_item (Location* location)
2707 double const x1 = _editor->frame_to_pixel (location->start());
2709 _points.front().set_x(x1);
2710 _points.back().set_x(x1);
2711 _line->property_points() = _points;
2714 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2716 _cumulative_x_drag (0),
2717 _cumulative_y_drag (0)
2719 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2725 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2727 Drag::start_grab (event, _editor->fader_cursor);
2729 // start the grab at the center of the control point so
2730 // the point doesn't 'jump' to the mouse after the first drag
2731 _time_axis_view_grab_x = _point->get_x();
2732 _time_axis_view_grab_y = _point->get_y();
2734 float const fraction = 1 - (_point->get_y() / _point->line().height());
2736 _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2738 _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2739 event->button.x + 10, event->button.y + 10);
2741 _editor->show_verbose_canvas_cursor ();
2745 ControlPointDrag::motion (GdkEvent* event, bool)
2747 double dx = _drags->current_pointer_x() - last_pointer_x();
2748 double dy = _drags->current_pointer_y() - last_pointer_y();
2750 if (event->button.state & Keyboard::SecondaryModifier) {
2755 /* coordinate in TimeAxisView's space */
2756 double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2757 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2759 // calculate zero crossing point. back off by .01 to stay on the
2760 // positive side of zero
2761 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2763 // make sure we hit zero when passing through
2764 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2768 if (_x_constrained) {
2769 cx = _time_axis_view_grab_x;
2771 if (_y_constrained) {
2772 cy = _time_axis_view_grab_y;
2775 _cumulative_x_drag = cx - _time_axis_view_grab_x;
2776 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2780 cy = min ((double) _point->line().height(), cy);
2782 nframes64_t cx_frames = _editor->unit_to_frame (cx);
2784 if (!_x_constrained) {
2785 _editor->snap_to_with_modifier (cx_frames, event);
2788 float const fraction = 1.0 - (cy / _point->line().height());
2790 bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2792 _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2794 _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2798 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2800 if (!movement_occurred) {
2804 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2805 _editor->reset_point_selection ();
2809 motion (event, false);
2811 _point->line().end_drag ();
2815 ControlPointDrag::aborted ()
2817 _point->line().reset ();
2821 ControlPointDrag::active (Editing::MouseMode m)
2823 if (m == Editing::MouseGain) {
2824 /* always active in mouse gain */
2828 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2829 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2832 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2835 _cumulative_y_drag (0)
2840 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2842 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2845 _item = &_line->grab_item ();
2847 /* need to get x coordinate in terms of parent (TimeAxisItemView)
2848 origin, and ditto for y.
2851 double cx = event->button.x;
2852 double cy = event->button.y;
2854 _line->parent_group().w2i (cx, cy);
2856 nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2861 if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2862 /* no adjacent points */
2866 Drag::start_grab (event, _editor->fader_cursor);
2868 /* store grab start in parent frame */
2870 _time_axis_view_grab_x = cx;
2871 _time_axis_view_grab_y = cy;
2873 double fraction = 1.0 - (cy / _line->height());
2875 _line->start_drag_line (before, after, fraction);
2877 _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2878 event->button.x + 10, event->button.y + 10);
2880 _editor->show_verbose_canvas_cursor ();
2884 LineDrag::motion (GdkEvent* event, bool)
2886 double dy = _drags->current_pointer_y() - last_pointer_y();
2888 if (event->button.state & Keyboard::SecondaryModifier) {
2892 double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2894 _cumulative_y_drag = cy - _time_axis_view_grab_y;
2897 cy = min ((double) _line->height(), cy);
2899 double const fraction = 1.0 - (cy / _line->height());
2903 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2909 /* we are ignoring x position for this drag, so we can just pass in anything */
2910 _line->drag_motion (0, fraction, true, push);
2912 _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2916 LineDrag::finished (GdkEvent* event, bool)
2918 motion (event, false);
2923 LineDrag::aborted ()
2929 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2931 Drag::start_grab (event);
2932 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2936 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2943 nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2945 nframes64_t grab = grab_frame ();
2946 if (Config->get_rubberbanding_snaps_to_grid ()) {
2947 _editor->snap_to_with_modifier (grab, event);
2950 /* base start and end on initial click position */
2960 if (_drags->current_pointer_y() < grab_y()) {
2961 y1 = _drags->current_pointer_y();
2964 y2 = _drags->current_pointer_y();
2969 if (start != end || y1 != y2) {
2971 double x1 = _editor->frame_to_pixel (start);
2972 double x2 = _editor->frame_to_pixel (end);
2974 _editor->rubberband_rect->property_x1() = x1;
2975 _editor->rubberband_rect->property_y1() = y1;
2976 _editor->rubberband_rect->property_x2() = x2;
2977 _editor->rubberband_rect->property_y2() = y2;
2979 _editor->rubberband_rect->show();
2980 _editor->rubberband_rect->raise_to_top();
2982 _editor->show_verbose_time_cursor (pf, 10);
2987 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2989 if (movement_occurred) {
2991 motion (event, false);
2994 if (_drags->current_pointer_y() < grab_y()) {
2995 y1 = _drags->current_pointer_y();
2998 y2 = _drags->current_pointer_y();
3003 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3006 _editor->begin_reversible_command (_("rubberband selection"));
3008 if (grab_frame() < last_pointer_frame()) {
3009 committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3011 committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3015 _editor->commit_reversible_command ();
3019 if (!getenv("ARDOUR_SAE")) {
3020 _editor->selection->clear_tracks();
3022 _editor->selection->clear_regions();
3023 _editor->selection->clear_points ();
3024 _editor->selection->clear_lines ();
3027 _editor->rubberband_rect->hide();
3031 RubberbandSelectDrag::aborted ()
3033 _editor->rubberband_rect->hide ();
3037 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3039 Drag::start_grab (event);
3041 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3045 TimeFXDrag::motion (GdkEvent* event, bool)
3047 RegionView* rv = _primary;
3049 nframes64_t const pf = adjusted_current_frame (event);
3051 if (pf > rv->region()->position()) {
3052 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3055 _editor->show_verbose_time_cursor (pf, 10);
3059 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3061 _primary->get_time_axis_view().hide_timestretch ();
3063 if (!movement_occurred) {
3067 if (last_pointer_frame() < _primary->region()->position()) {
3068 /* backwards drag of the left edge - not usable */
3072 nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3074 float percentage = (double) newlen / (double) _primary->region()->length();
3076 #ifndef USE_RUBBERBAND
3077 // Soundtouch uses percentage / 100 instead of normal (/ 1)
3078 if (_primary->region()->data_type() == DataType::AUDIO) {
3079 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3083 _editor->begin_reversible_command (_("timestretch"));
3085 // XXX how do timeFX on multiple regions ?
3090 if (!_editor->time_stretch (rs, percentage) == 0) {
3091 error << _("An error occurred while executing time stretch operation") << endmsg;
3096 TimeFXDrag::aborted ()
3098 _primary->get_time_axis_view().hide_timestretch ();
3103 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3105 Drag::start_grab (event);
3109 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3111 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3115 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3117 if (movement_occurred && _editor->session()) {
3118 /* make sure we stop */
3119 _editor->session()->request_transport_speed (0.0);
3124 ScrubDrag::aborted ()
3129 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3133 , _original_pointer_time_axis (-1)
3134 , _last_pointer_time_axis (-1)
3140 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3142 nframes64_t start = 0;
3143 nframes64_t end = 0;
3145 if (_editor->session() == 0) {
3149 Gdk::Cursor* cursor = 0;
3151 switch (_operation) {
3152 case CreateSelection:
3153 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3158 cursor = _editor->selector_cursor;
3159 Drag::start_grab (event, cursor);
3162 case SelectionStartTrim:
3163 if (_editor->clicked_axisview) {
3164 _editor->clicked_axisview->order_selection_trims (_item, true);
3166 Drag::start_grab (event, _editor->trimmer_cursor);
3167 start = _editor->selection->time[_editor->clicked_selection].start;
3168 _pointer_frame_offset = grab_frame() - start;
3171 case SelectionEndTrim:
3172 if (_editor->clicked_axisview) {
3173 _editor->clicked_axisview->order_selection_trims (_item, false);
3175 Drag::start_grab (event, _editor->trimmer_cursor);
3176 end = _editor->selection->time[_editor->clicked_selection].end;
3177 _pointer_frame_offset = grab_frame() - end;
3181 start = _editor->selection->time[_editor->clicked_selection].start;
3182 Drag::start_grab (event, cursor);
3183 _pointer_frame_offset = grab_frame() - start;
3187 if (_operation == SelectionMove) {
3188 _editor->show_verbose_time_cursor (start, 10);
3190 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3193 _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3197 SelectionDrag::motion (GdkEvent* event, bool first_move)
3199 nframes64_t start = 0;
3200 nframes64_t end = 0;
3203 pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3204 if (pending_time_axis.first == 0) {
3208 nframes64_t const pending_position = adjusted_current_frame (event);
3210 /* only alter selection if things have changed */
3212 if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3216 switch (_operation) {
3217 case CreateSelection:
3219 nframes64_t grab = grab_frame ();
3222 _editor->snap_to (grab);
3225 if (pending_position < grab_frame()) {
3226 start = pending_position;
3229 end = pending_position;
3233 /* first drag: Either add to the selection
3234 or create a new selection
3240 /* adding to the selection */
3241 _editor->selection->add (_editor->clicked_axisview);
3242 _editor->clicked_selection = _editor->selection->add (start, end);
3247 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3248 _editor->selection->set (_editor->clicked_axisview);
3251 _editor->clicked_selection = _editor->selection->set (start, end);
3255 /* select the track that we're in */
3256 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3257 _editor->selection->add (pending_time_axis.first);
3258 _added_time_axes.push_back (pending_time_axis.first);
3261 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3262 tracks that we selected in the first place.
3265 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3266 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3268 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3269 while (i != _added_time_axes.end()) {
3271 list<TimeAxisView*>::iterator tmp = i;
3274 if ((*i)->order() < min_order || (*i)->order() > max_order) {
3275 _editor->selection->remove (*i);
3276 _added_time_axes.remove (*i);
3285 case SelectionStartTrim:
3287 start = _editor->selection->time[_editor->clicked_selection].start;
3288 end = _editor->selection->time[_editor->clicked_selection].end;
3290 if (pending_position > end) {
3293 start = pending_position;
3297 case SelectionEndTrim:
3299 start = _editor->selection->time[_editor->clicked_selection].start;
3300 end = _editor->selection->time[_editor->clicked_selection].end;
3302 if (pending_position < start) {
3305 end = pending_position;
3312 start = _editor->selection->time[_editor->clicked_selection].start;
3313 end = _editor->selection->time[_editor->clicked_selection].end;
3315 length = end - start;
3317 start = pending_position;
3318 _editor->snap_to (start);
3320 end = start + length;
3325 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3326 _editor->start_canvas_autoscroll (1, 0);
3330 _editor->selection->replace (_editor->clicked_selection, start, end);
3333 if (_operation == SelectionMove) {
3334 _editor->show_verbose_time_cursor(start, 10);
3336 _editor->show_verbose_time_cursor(pending_position, 10);
3341 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3343 Session* s = _editor->session();
3345 if (movement_occurred) {
3346 motion (event, false);
3347 /* XXX this is not object-oriented programming at all. ick */
3348 if (_editor->selection->time.consolidate()) {
3349 _editor->selection->TimeChanged ();
3352 /* XXX what if its a music time selection? */
3353 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3354 s->request_play_range (&_editor->selection->time, true);
3359 /* just a click, no pointer movement.*/
3361 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3362 _editor->selection->clear_time();
3365 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3366 _editor->selection->set (_editor->clicked_axisview);
3369 if (s && s->get_play_range () && s->transport_rolling()) {
3370 s->request_stop (false, false);
3375 _editor->stop_canvas_autoscroll ();
3379 SelectionDrag::aborted ()
3384 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3389 _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3390 _drag_rect->hide ();
3392 _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3393 _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3397 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3399 if (_editor->session() == 0) {
3403 Gdk::Cursor* cursor = 0;
3405 if (!_editor->temp_location) {
3406 _editor->temp_location = new Location;
3409 switch (_operation) {
3410 case CreateRangeMarker:
3411 case CreateTransportMarker:
3412 case CreateCDMarker:
3414 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3419 cursor = _editor->selector_cursor;
3423 Drag::start_grab (event, cursor);
3425 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3429 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3431 nframes64_t start = 0;
3432 nframes64_t end = 0;
3433 ArdourCanvas::SimpleRect *crect;
3435 switch (_operation) {
3436 case CreateRangeMarker:
3437 crect = _editor->range_bar_drag_rect;
3439 case CreateTransportMarker:
3440 crect = _editor->transport_bar_drag_rect;
3442 case CreateCDMarker:
3443 crect = _editor->cd_marker_bar_drag_rect;
3446 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3451 nframes64_t const pf = adjusted_current_frame (event);
3453 if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3454 nframes64_t grab = grab_frame ();
3455 _editor->snap_to (grab);
3457 if (pf < grab_frame()) {
3465 /* first drag: Either add to the selection
3466 or create a new selection.
3471 _editor->temp_location->set (start, end);
3475 update_item (_editor->temp_location);
3477 //_drag_rect->raise_to_top();
3482 if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3483 _editor->start_canvas_autoscroll (1, 0);
3487 _editor->temp_location->set (start, end);
3489 double x1 = _editor->frame_to_pixel (start);
3490 double x2 = _editor->frame_to_pixel (end);
3491 crect->property_x1() = x1;
3492 crect->property_x2() = x2;
3494 update_item (_editor->temp_location);
3497 _editor->show_verbose_time_cursor (pf, 10);
3502 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3504 Location * newloc = 0;
3508 if (movement_occurred) {
3509 motion (event, false);
3512 switch (_operation) {
3513 case CreateRangeMarker:
3514 case CreateCDMarker:
3516 _editor->begin_reversible_command (_("new range marker"));
3517 XMLNode &before = _editor->session()->locations()->get_state();
3518 _editor->session()->locations()->next_available_name(rangename,"unnamed");
3519 if (_operation == CreateCDMarker) {
3520 flags = Location::IsRangeMarker | Location::IsCDMarker;
3521 _editor->cd_marker_bar_drag_rect->hide();
3524 flags = Location::IsRangeMarker;
3525 _editor->range_bar_drag_rect->hide();
3527 newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3528 _editor->session()->locations()->add (newloc, true);
3529 XMLNode &after = _editor->session()->locations()->get_state();
3530 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3531 _editor->commit_reversible_command ();
3535 case CreateTransportMarker:
3536 // popup menu to pick loop or punch
3537 _editor->new_transport_marker_context_menu (&event->button, _item);
3541 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3543 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3548 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3550 if (end == max_frames) {
3551 end = _editor->session()->current_end_frame ();
3554 if (start == max_frames) {
3555 start = _editor->session()->current_start_frame ();
3558 switch (_editor->mouse_mode) {
3560 /* find the two markers on either side and then make the selection from it */
3561 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3565 /* find the two markers on either side of the click and make the range out of it */
3566 _editor->selection->set (start, end);
3575 _editor->stop_canvas_autoscroll ();
3579 RangeMarkerBarDrag::aborted ()
3585 RangeMarkerBarDrag::update_item (Location* location)
3587 double const x1 = _editor->frame_to_pixel (location->start());
3588 double const x2 = _editor->frame_to_pixel (location->end());
3590 _drag_rect->property_x1() = x1;
3591 _drag_rect->property_x2() = x2;
3595 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3597 Drag::start_grab (event, _editor->zoom_cursor);
3598 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3602 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3607 nframes64_t const pf = adjusted_current_frame (event);
3609 nframes64_t grab = grab_frame ();
3610 _editor->snap_to_with_modifier (grab, event);
3612 /* base start and end on initial click position */
3624 _editor->zoom_rect->show();
3625 _editor->zoom_rect->raise_to_top();
3628 _editor->reposition_zoom_rect(start, end);
3630 _editor->show_verbose_time_cursor (pf, 10);
3635 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3637 if (movement_occurred) {
3638 motion (event, false);
3640 if (grab_frame() < last_pointer_frame()) {
3641 _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3643 _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3646 _editor->temporal_zoom_to_frame (false, grab_frame());
3648 temporal_zoom_step (false);
3649 center_screen (grab_frame());
3653 _editor->zoom_rect->hide();
3657 MouseZoomDrag::aborted ()
3659 _editor->zoom_rect->hide ();
3662 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3665 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3666 region = &cnote->region_view();
3670 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3672 Drag::start_grab (event);
3675 drag_delta_note = 0;
3680 event_x = _drags->current_pointer_x();
3681 event_y = _drags->current_pointer_y();
3683 _item->property_parent().get_value()->w2i(event_x, event_y);
3685 last_x = region->snap_to_pixel(event_x);
3688 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3690 if (!(was_selected = cnote->selected())) {
3692 /* tertiary-click means extend selection - we'll do that on button release,
3693 so don't add it here, because otherwise we make it hard to figure
3694 out the "extend-to" range.
3697 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3700 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3703 region->note_selected (cnote, true);
3705 region->unique_select (cnote);
3712 NoteDrag::motion (GdkEvent*, bool)
3714 MidiStreamView* streamview = region->midi_stream_view();
3718 event_x = _drags->current_pointer_x();
3719 event_y = _drags->current_pointer_y();
3721 _item->property_parent().get_value()->w2i(event_x, event_y);
3723 event_x = region->snap_to_pixel(event_x);
3725 double dx = event_x - last_x;
3726 double dy = event_y - last_y;
3731 // Snap to note rows
3733 if (abs (dy) < streamview->note_height()) {
3736 int8_t this_delta_note;
3738 this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3740 this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3742 drag_delta_note -= this_delta_note;
3743 dy = streamview->note_height() * this_delta_note;
3744 last_y = last_y + dy;
3748 region->move_selection (dx, dy);
3750 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3752 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3753 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3754 _editor->show_verbose_canvas_cursor_with (buf);
3759 NoteDrag::finished (GdkEvent* ev, bool moved)
3761 ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3764 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3767 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3769 region->note_deselected (cnote);
3772 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3773 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3775 if (!extend && !add && region->selection_size() > 1) {
3776 region->unique_select(cnote);
3777 } else if (extend) {
3778 region->note_selected (cnote, true, true);
3780 /* it was added during button press */
3785 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3790 NoteDrag::aborted ()
3795 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3798 , _nothing_to_drag (false)
3800 _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3803 _line = _atav->line ();
3807 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3809 Drag::start_grab (event, cursor);
3811 list<ControlPoint*> points;
3813 XMLNode* state = &_line->get_state ();
3815 if (_ranges.empty()) {
3817 uint32_t const N = _line->npoints ();
3818 for (uint32_t i = 0; i < N; ++i) {
3819 points.push_back (_line->nth (i));
3824 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3825 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3827 /* fade into and out of the region that we're dragging;
3828 64 samples length plucked out of thin air.
3830 nframes64_t const h = (j->start + j->end) / 2;
3831 nframes64_t a = j->start + 64;
3835 nframes64_t b = j->end - 64;
3840 the_list->add (j->start, the_list->eval (j->start));
3841 _line->add_always_in_view (j->start);
3842 the_list->add (a, the_list->eval (a));
3843 _line->add_always_in_view (a);
3844 the_list->add (b, the_list->eval (b));
3845 _line->add_always_in_view (b);
3846 the_list->add (j->end, the_list->eval (j->end));
3847 _line->add_always_in_view (j->end);
3850 uint32_t const N = _line->npoints ();
3851 for (uint32_t i = 0; i < N; ++i) {
3853 ControlPoint* p = _line->nth (i);
3855 list<AudioRange>::const_iterator j = _ranges.begin ();
3856 while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3860 if (j != _ranges.end()) {
3861 points.push_back (p);
3866 if (points.empty()) {
3867 _nothing_to_drag = true;
3871 _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3875 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3877 if (_nothing_to_drag) {
3881 float const f = 1 - (_drags->current_pointer_y() / _line->height());
3883 /* we are ignoring x position for this drag, so we can just pass in anything */
3884 _line->drag_motion (0, f, true, false);
3888 AutomationRangeDrag::finished (GdkEvent* event, bool)
3890 if (_nothing_to_drag) {
3894 motion (event, false);
3896 _line->clear_always_in_view ();
3900 AutomationRangeDrag::aborted ()
3902 _line->clear_always_in_view ();