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.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_x (0.0)
86 , _current_pointer_y (0.0)
87 , _current_pointer_frame (0)
88 , _old_follow_playhead (false)
92 DragManager::~DragManager ()
97 /** Call abort for each active drag */
103 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 if (!_drags.empty ()) {
109 _editor->set_follow_playhead (_old_follow_playhead, false);
113 _editor->abort_reversible_command();
119 DragManager::add (Drag* d)
121 d->set_manager (this);
122 _drags.push_back (d);
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
128 d->set_manager (this);
129 _drags.push_back (d);
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
136 /* Prevent follow playhead during the drag to be nice to the user */
137 _old_follow_playhead = _editor->follow_playhead ();
138 _editor->set_follow_playhead (false);
140 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
142 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 (*i)->start_grab (e, c);
147 /** Call end_grab for each active drag.
148 * @return true if any drag reported movement having occurred.
151 DragManager::end_grab (GdkEvent* e)
156 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157 bool const t = (*i)->end_grab (e);
168 _editor->set_follow_playhead (_old_follow_playhead, false);
174 DragManager::mark_double_click ()
176 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177 (*i)->set_double_click (true);
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
186 /* calling this implies that we expect the event to have canvas
189 * Can we guarantee that this is true?
192 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
194 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195 bool const t = (*i)->motion_handler (e, from_autoscroll);
196 /* run all handlers; return true if at least one of them
197 returns true (indicating that the event has been handled).
209 DragManager::have_item (ArdourCanvas::Item* i) const
211 list<Drag*>::const_iterator j = _drags.begin ();
212 while (j != _drags.end() && (*j)->item () != i) {
216 return j != _drags.end ();
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
223 , _pointer_frame_offset (0)
224 , _x_constrained (false)
225 , _y_constrained (false)
226 , _was_rolling (false)
227 , _trackview_only (trackview_only)
228 , _move_threshold_passed (false)
229 , _starting_point_passed (false)
230 , _initially_vertical (false)
231 , _was_double_click (false)
234 , _last_pointer_x (0.0)
235 , _last_pointer_y (0.0)
236 , _raw_grab_frame (0)
238 , _last_pointer_frame (0)
245 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
251 _cursor_ctx = CursorContext::create (*_editor, cursor);
253 _cursor_ctx->change (cursor);
260 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
263 /* we set up x/y dragging constraints on first move */
265 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
267 setup_pointer_frame_offset ();
268 _grab_frame = adjusted_frame (_raw_grab_frame, event);
269 _last_pointer_frame = _grab_frame;
270 _last_pointer_x = _grab_x;
272 if (_trackview_only) {
273 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
276 _last_pointer_y = _grab_y;
280 if (!_editor->cursors()->is_invalid (cursor)) {
281 /* CAIROCANVAS need a variant here that passes *cursor */
282 _cursor_ctx = CursorContext::create (*_editor, cursor);
285 if (_editor->session() && _editor->session()->transport_rolling()) {
288 _was_rolling = false;
291 switch (_editor->snap_type()) {
292 case SnapToRegionStart:
293 case SnapToRegionEnd:
294 case SnapToRegionSync:
295 case SnapToRegionBoundary:
296 _editor->build_region_boundary_cache ();
303 /** Call to end a drag `successfully'. Ungrabs item and calls
304 * subclass' finished() method.
306 * @param event GDK event, or 0.
307 * @return true if some movement occurred, otherwise false.
310 Drag::end_grab (GdkEvent* event)
312 _editor->stop_canvas_autoscroll ();
316 finished (event, _move_threshold_passed);
318 _editor->verbose_cursor()->hide ();
321 return _move_threshold_passed;
325 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
329 if (f > _pointer_frame_offset) {
330 pos = f - _pointer_frame_offset;
334 _editor->snap_to_with_modifier (pos, event);
341 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
343 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
347 Drag::snap_delta (guint state) const
349 if (ArdourKeyboard::indicates_snap_delta (state)) {
357 Drag::current_pointer_x() const
359 return _drags->current_pointer_x ();
363 Drag::current_pointer_y () const
365 if (!_trackview_only) {
366 return _drags->current_pointer_y ();
369 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
373 Drag::setup_snap_delta (framepos_t pos)
375 framepos_t temp = pos;
376 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
377 _snap_delta = temp - pos;
381 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
383 /* check to see if we have moved in any way that matters since the last motion event */
384 if (_move_threshold_passed &&
385 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
386 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
390 pair<framecnt_t, int> const threshold = move_threshold ();
392 bool const old_move_threshold_passed = _move_threshold_passed;
394 if (!_move_threshold_passed) {
396 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
397 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
399 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
402 if (active (_editor->mouse_mode) && _move_threshold_passed) {
404 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
406 if (old_move_threshold_passed != _move_threshold_passed) {
410 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
411 _initially_vertical = true;
413 _initially_vertical = false;
415 /** check constraints for this drag.
416 * Note that the current convention is to use "contains" for
417 * key modifiers during motion and "equals" when initiating a drag.
418 * In this case we haven't moved yet, so "equals" applies here.
420 if (Config->get_edit_mode() != Lock) {
421 if (event->motion.state & Gdk::BUTTON2_MASK) {
422 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
423 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
424 _x_constrained = false;
425 _y_constrained = true;
427 _x_constrained = true;
428 _y_constrained = false;
430 } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
431 // if dragging normally, the motion is constrained to the first direction of movement.
432 if (_initially_vertical) {
433 _x_constrained = true;
434 _y_constrained = false;
436 _x_constrained = false;
437 _y_constrained = true;
441 if (event->button.state & Gdk::BUTTON2_MASK) {
442 _x_constrained = false;
444 _x_constrained = true;
446 _y_constrained = false;
450 if (!from_autoscroll) {
451 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
454 if (!_editor->autoscroll_active() || from_autoscroll) {
457 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
459 motion (event, first_move && !_starting_point_passed);
461 if (first_move && !_starting_point_passed) {
462 _starting_point_passed = true;
465 _last_pointer_x = _drags->current_pointer_x ();
466 _last_pointer_y = current_pointer_y ();
467 _last_pointer_frame = adjusted_current_frame (event, false);
477 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
485 aborted (_move_threshold_passed);
487 _editor->stop_canvas_autoscroll ();
488 _editor->verbose_cursor()->hide ();
492 Drag::show_verbose_cursor_time (framepos_t frame)
494 _editor->verbose_cursor()->set_time (frame);
495 _editor->verbose_cursor()->show ();
499 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
501 _editor->verbose_cursor()->set_duration (start, end);
502 _editor->verbose_cursor()->show ();
506 Drag::show_verbose_cursor_text (string const & text)
508 _editor->verbose_cursor()->set (text);
509 _editor->verbose_cursor()->show ();
512 boost::shared_ptr<Region>
513 Drag::add_midi_region (MidiTimeAxisView* view, bool commit)
515 if (_editor->session()) {
516 const TempoMap& map (_editor->session()->tempo_map());
517 framecnt_t pos = grab_frame();
518 /* not that the frame rate used here can be affected by pull up/down which
521 framecnt_t len = map.frame_at_beat (map.beat_at_frame (pos) + 1.0) - pos;
522 return view->add_region (grab_frame(), len, commit);
525 return boost::shared_ptr<Region>();
528 struct PresentationInfoTimeAxisViewSorter {
529 bool operator() (TimeAxisView* a, TimeAxisView* b) {
530 return a->presentation_info().order() < b->presentation_info().order();
534 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
539 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
541 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
542 as some of the regions we are dragging may be on such tracks.
545 TrackViewList track_views = _editor->track_views;
546 track_views.sort (PresentationInfoTimeAxisViewSorter ());
548 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
549 _time_axis_views.push_back (*i);
551 TimeAxisView::Children children_list = (*i)->get_child_list ();
552 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
553 _time_axis_views.push_back (j->get());
557 /* the list of views can be empty at this point if this is a region list-insert drag
560 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
561 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
564 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
568 RegionDrag::region_going_away (RegionView* v)
570 list<DraggingView>::iterator i = _views.begin ();
571 while (i != _views.end() && i->view != v) {
575 if (i != _views.end()) {
580 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
581 * or -1 if it is not found.
584 RegionDrag::find_time_axis_view (TimeAxisView* t) const
587 int const N = _time_axis_views.size ();
588 while (i < N && _time_axis_views[i] != t) {
592 if (_time_axis_views[i] != t) {
599 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
600 : RegionDrag (e, i, p, v)
602 , _ignore_video_lock (false)
604 , _last_pointer_time_axis_view (0)
605 , _last_pointer_layer (0)
610 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
614 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
616 Drag::start_grab (event, cursor);
617 setup_snap_delta (_last_frame_position);
619 show_verbose_cursor_time (_last_frame_position);
621 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
623 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
624 assert(_last_pointer_time_axis_view >= 0);
625 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
628 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
629 _ignore_video_lock = true;
633 /* cross track dragging seems broken here. disabled for now. */
634 _y_constrained = true;
639 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
641 /* compute the amount of pointer motion in frames, and where
642 the region would be if we moved it by that much.
644 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
646 framepos_t sync_frame;
647 framecnt_t sync_offset;
650 sync_offset = _primary->region()->sync_offset (sync_dir);
652 /* we don't handle a sync point that lies before zero.
654 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
656 framecnt_t const sd = snap_delta (event->button.state);
657 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
659 _editor->snap_to_with_modifier (sync_frame, event);
661 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
664 *pending_region_position = _last_frame_position;
667 if (*pending_region_position > max_framepos - _primary->region()->length()) {
668 *pending_region_position = _last_frame_position;
673 bool const x_move_allowed = !_x_constrained;
675 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
677 /* x movement since last time (in pixels) */
678 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
680 /* total x movement */
681 framecnt_t total_dx = *pending_region_position;
682 if (regions_came_from_canvas()) {
683 total_dx = total_dx - grab_frame ();
686 /* check that no regions have gone off the start of the session */
687 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
688 if ((i->view->region()->position() + total_dx) < 0) {
690 *pending_region_position = _last_frame_position;
701 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
707 const int tavsize = _time_axis_views.size();
708 const int dt = delta > 0 ? +1 : -1;
710 int target = start + delta - skip;
712 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
713 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
715 while (current >= 0 && current != target) {
717 if (current < 0 && dt < 0) {
720 if (current >= tavsize && dt > 0) {
723 if (current < 0 || current >= tavsize) {
727 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
728 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
732 if (distance_only && current == start + delta) {
740 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
742 if (_y_constrained) {
746 const int tavsize = _time_axis_views.size();
747 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
748 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
749 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
751 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
752 /* already in the drop zone */
753 if (delta_track >= 0) {
754 /* downward motion - OK if others are still not in the dropzone */
763 } else if (n >= tavsize) {
764 /* downward motion into drop zone. That's fine. */
768 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
769 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
770 /* not a track, or the wrong type */
774 double const l = i->layer + delta_layer;
776 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
777 mode to allow the user to place a region below another on layer 0.
779 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
780 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
781 If it has, the layers will be munged later anyway, so it's ok.
787 /* all regions being dragged are ok with this change */
791 struct DraggingViewSorter {
792 bool operator() (const DraggingView& a, const DraggingView& b) {
793 return a.time_axis_view < b.time_axis_view;
798 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
800 double delta_layer = 0;
801 int delta_time_axis_view = 0;
802 int current_pointer_time_axis_view = -1;
804 assert (!_views.empty ());
806 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
808 /* Find the TimeAxisView that the pointer is now over */
809 const double cur_y = current_pointer_y ();
810 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
811 TimeAxisView* tv = r.first;
813 if (!tv && cur_y < 0) {
814 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
818 /* find drop-zone y-position */
819 Coord last_track_bottom_edge;
820 last_track_bottom_edge = 0;
821 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
822 if (!(*t)->hidden()) {
823 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
828 if (tv && tv->view()) {
829 /* the mouse is over a track */
830 double layer = r.second;
832 if (first_move && tv->view()->layer_display() == Stacked) {
833 tv->view()->set_layer_display (Expanded);
836 /* Here's the current pointer position in terms of time axis view and layer */
837 current_pointer_time_axis_view = find_time_axis_view (tv);
838 assert(current_pointer_time_axis_view >= 0);
840 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
842 /* Work out the change in y */
844 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
845 if (!rtv || !rtv->is_track()) {
846 /* ignore non-tracks early on. we can't move any regions on them */
847 } else if (_last_pointer_time_axis_view < 0) {
848 /* Was in the drop-zone, now over a track.
849 * Hence it must be an upward move (from the bottom)
851 * track_index is still -1, so delta must be set to
852 * move up the correct number of tracks from the bottom.
854 * This is necessary because steps may be skipped if
855 * the bottom-most track is not a valid target and/or
856 * if there are hidden tracks at the bottom.
857 * Hence the initial offset (_ddropzone) as well as the
858 * last valid pointer position (_pdropzone) need to be
859 * taken into account.
861 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
863 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
866 /* TODO needs adjustment per DraggingView,
868 * e.g. select one region on the top-layer of a track
869 * and one region which is at the bottom-layer of another track
872 * Indicated drop-zones and layering is wrong.
873 * and may infer additional layers on the target-track
874 * (depending how many layers the original track had).
876 * Or select two regions (different layers) on a same track,
877 * move across a non-layer track.. -> layering info is lost.
878 * on drop either of the regions may be on top.
880 * Proposed solution: screw it :) well,
881 * don't use delta_layer, use an absolute value
882 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
883 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
884 * 3) iterate over all DraggingView, find the one that is over the track with most layers
885 * 4) proportionally scale layer to layers available on target
887 delta_layer = current_pointer_layer - _last_pointer_layer;
890 /* for automation lanes, there is a TimeAxisView but no ->view()
891 * if (!tv) -> dropzone
893 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
894 /* Moving into the drop-zone.. */
895 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
896 /* delta_time_axis_view may not be sufficient to move into the DZ
897 * the mouse may enter it, but it may not be a valid move due to
900 * -> remember the delta needed to move into the dropzone
902 _ddropzone = delta_time_axis_view;
903 /* ..but subtract hidden tracks (or routes) at the bottom.
904 * we silently move mover them
906 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
907 - _time_axis_views.size();
909 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
910 /* move around inside the zone.
911 * This allows to move further down until all regions are in the zone.
913 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
914 assert(ptr_y >= last_track_bottom_edge);
915 assert(_ddropzone > 0);
917 /* calculate mouse position in 'tracks' below last track. */
918 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
919 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
921 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
923 delta_time_axis_view = dzpos - _pdropzone;
924 } else if (dzpos < _pdropzone && _ndropzone > 0) {
925 // move up inside the DZ
926 delta_time_axis_view = dzpos - _pdropzone;
930 /* Work out the change in x */
931 framepos_t pending_region_position;
932 double const x_delta = compute_x_delta (event, &pending_region_position);
933 _last_frame_position = pending_region_position;
935 /* calculate hidden tracks in current y-axis delta */
937 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
938 /* The mouse is more than one track below the dropzone.
939 * distance calculation is not needed (and would not work, either
940 * because the dropzone is "packed").
942 * Except when [partially] moving regions out of dropzone in a large step.
943 * (the mouse may or may not remain in the DZ)
944 * Hidden tracks at the bottom of the TAV need to be skipped.
946 * This also handles the case if the mouse entered the DZ
947 * in a large step (exessive delta), either due to fast-movement,
948 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
950 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
951 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
953 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
954 -_time_axis_views.size() - dt;
957 else if (_last_pointer_time_axis_view < 0) {
958 /* Moving out of the zone. Check for hidden tracks at the bottom. */
959 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
960 -_time_axis_views.size() - delta_time_axis_view;
962 /* calculate hidden tracks that are skipped by the pointer movement */
963 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
964 - _last_pointer_time_axis_view
965 - delta_time_axis_view;
968 /* Verify change in y */
969 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
970 /* this y movement is not allowed, so do no y movement this time */
971 delta_time_axis_view = 0;
976 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
977 /* haven't reached next snap point, and we're not switching
978 trackviews nor layers. nothing to do.
983 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
984 PlaylistDropzoneMap playlist_dropzone_map;
985 _ndropzone = 0; // number of elements currently in the dropzone
988 /* sort views by time_axis.
989 * This retains track order in the dropzone, regardless
990 * of actual selection order
992 _views.sort (DraggingViewSorter());
994 /* count number of distinct tracks of all regions
995 * being dragged, used for dropzone.
998 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
999 if (i->time_axis_view != prev_track) {
1000 prev_track = i->time_axis_view;
1006 _views.back().time_axis_view -
1007 _views.front().time_axis_view;
1009 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1010 - _views.back().time_axis_view;
1012 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1016 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1018 RegionView* rv = i->view;
1023 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1030 /* reparent the regionview into a group above all
1034 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1035 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1036 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1037 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1038 /* move the item so that it continues to appear at the
1039 same location now that its parent has changed.
1041 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1044 /* If we have moved tracks, we'll fudge the layer delta so that the
1045 region gets moved back onto layer 0 on its new track; this avoids
1046 confusion when dragging regions from non-zero layers onto different
1049 double this_delta_layer = delta_layer;
1050 if (delta_time_axis_view != 0) {
1051 this_delta_layer = - i->layer;
1054 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1056 int track_index = i->time_axis_view + this_delta_time_axis_view;
1057 assert(track_index >= 0);
1059 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1060 /* Track is in the Dropzone */
1062 i->time_axis_view = track_index;
1063 assert(i->time_axis_view >= (int) _time_axis_views.size());
1066 double yposition = 0;
1067 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1068 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1071 /* store index of each new playlist as a negative count, starting at -1 */
1073 if (pdz == playlist_dropzone_map.end()) {
1074 /* compute where this new track (which doesn't exist yet) will live
1077 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1079 /* How high is this region view ? */
1081 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1082 ArdourCanvas::Rect bbox;
1085 bbox = obbox.get ();
1088 last_track_bottom_edge += bbox.height();
1090 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1093 yposition = pdz->second;
1096 /* values are zero or negative, hence the use of min() */
1097 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1102 /* The TimeAxisView that this region is now over */
1103 TimeAxisView* current_tv = _time_axis_views[track_index];
1105 /* Ensure it is moved from stacked -> expanded if appropriate */
1106 if (current_tv->view()->layer_display() == Stacked) {
1107 current_tv->view()->set_layer_display (Expanded);
1110 /* We're only allowed to go -ve in layer on Expanded views */
1111 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1112 this_delta_layer = - i->layer;
1116 rv->set_height (current_tv->view()->child_height ());
1118 /* Update show/hidden status as the region view may have come from a hidden track,
1119 or have moved to one.
1121 if (current_tv->hidden ()) {
1122 rv->get_canvas_group()->hide ();
1124 rv->get_canvas_group()->show ();
1127 /* Update the DraggingView */
1128 i->time_axis_view = track_index;
1129 i->layer += this_delta_layer;
1132 _editor->mouse_brush_insert_region (rv, pending_region_position);
1136 /* Get the y coordinate of the top of the track that this region is now over */
1137 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1139 /* And adjust for the layer that it should be on */
1140 StreamView* cv = current_tv->view ();
1141 switch (cv->layer_display ()) {
1145 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1148 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1152 /* need to get the parent of the regionview
1153 * canvas group and get its position in
1154 * equivalent coordinate space as the trackview
1155 * we are now dragging over.
1158 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1163 /* Now move the region view */
1164 rv->move (x_delta, y_delta);
1166 } /* foreach region */
1168 _total_x_delta += x_delta;
1170 if (x_delta != 0 && !_brushing) {
1171 show_verbose_cursor_time (_last_frame_position);
1174 /* keep track of pointer movement */
1176 /* the pointer is currently over a time axis view */
1178 if (_last_pointer_time_axis_view < 0) {
1179 /* last motion event was not over a time axis view
1180 * or last y-movement out of the dropzone was not valid
1183 if (delta_time_axis_view < 0) {
1184 /* in the drop zone, moving up */
1186 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1187 * We do not use negative _last_pointer_time_axis_view because
1188 * the dropzone is "packed" (the actual track offset is ignored)
1190 * As opposed to the actual number
1191 * of elements in the dropzone (_ndropzone)
1192 * _pdropzone is not constrained. This is necessary
1193 * to allow moving multiple regions with y-distance
1196 * There can be 0 elements in the dropzone,
1197 * even though the drag-pointer is inside the DZ.
1200 * [ Audio-track, Midi-track, Audio-track, DZ ]
1201 * move regions from both audio tracks at the same time into the
1202 * DZ by grabbing the region in the bottom track.
1204 assert(current_pointer_time_axis_view >= 0);
1205 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1209 /* only move out of the zone if the movement is OK */
1210 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1211 assert(delta_time_axis_view < 0);
1212 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1213 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1214 * the current position can be calculated as follows:
1216 // a well placed oofus attack can still throw this off.
1217 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1218 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1221 /* last motion event was also over a time axis view */
1222 _last_pointer_time_axis_view += delta_time_axis_view;
1223 assert(_last_pointer_time_axis_view >= 0);
1228 /* the pointer is not over a time axis view */
1229 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1230 _pdropzone += delta_time_axis_view - delta_skip;
1231 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1234 _last_pointer_layer += delta_layer;
1238 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1240 if (_copy && first_move) {
1241 if (_x_constrained && !_brushing) {
1242 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1243 } else if (!_brushing) {
1244 _editor->begin_reversible_command (Operations::region_copy);
1245 } else if (_brushing) {
1246 _editor->begin_reversible_command (Operations::drag_region_brush);
1248 /* duplicate the regionview(s) and region(s) */
1250 list<DraggingView> new_regionviews;
1252 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1254 RegionView* rv = i->view;
1255 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1256 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1258 const boost::shared_ptr<const Region> original = rv->region();
1259 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1260 region_copy->set_position (original->position());
1261 /* need to set this so that the drop zone code can work. This doesn't
1262 actually put the region into the playlist, but just sets a weak pointer
1265 region_copy->set_playlist (original->playlist());
1269 boost::shared_ptr<AudioRegion> audioregion_copy
1270 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1272 nrv = new AudioRegionView (*arv, audioregion_copy);
1274 boost::shared_ptr<MidiRegion> midiregion_copy
1275 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1276 nrv = new MidiRegionView (*mrv, midiregion_copy);
1281 nrv->get_canvas_group()->show ();
1282 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1284 /* swap _primary to the copy */
1286 if (rv == _primary) {
1290 /* ..and deselect the one we copied */
1292 rv->set_selected (false);
1295 if (!new_regionviews.empty()) {
1297 /* reflect the fact that we are dragging the copies */
1299 _views = new_regionviews;
1301 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1304 } else if (!_copy && first_move) {
1305 if (_x_constrained && !_brushing) {
1306 _editor->begin_reversible_command (_("fixed time region drag"));
1307 } else if (!_brushing) {
1308 _editor->begin_reversible_command (Operations::region_drag);
1309 } else if (_brushing) {
1310 _editor->begin_reversible_command (Operations::drag_region_brush);
1313 RegionMotionDrag::motion (event, first_move);
1317 RegionMotionDrag::finished (GdkEvent *, bool)
1319 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1320 if (!(*i)->view()) {
1324 if ((*i)->view()->layer_display() == Expanded) {
1325 (*i)->view()->set_layer_display (Stacked);
1331 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1333 RegionMotionDrag::finished (ev, movement_occurred);
1335 if (!movement_occurred) {
1339 if (was_double_click() && !_views.empty()) {
1340 DraggingView dv = _views.front();
1341 dv.view->show_region_editor ();
1348 assert (!_views.empty ());
1350 /* We might have hidden region views so that they weren't visible during the drag
1351 (when they have been reparented). Now everything can be shown again, as region
1352 views are back in their track parent groups.
1354 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1355 i->view->get_canvas_group()->show ();
1358 bool const changed_position = (_last_frame_position != _primary->region()->position());
1359 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1360 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1380 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1384 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1386 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1391 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1392 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1393 uint32_t output_chan = region->n_channels();
1394 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1395 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1397 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1398 RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1400 rtav->set_height (original->current_height());
1404 ChanCount one_midi_port (DataType::MIDI, 1);
1405 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1406 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1407 (ARDOUR::Plugin::PresetRecord*) 0,
1408 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1409 RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1411 rtav->set_height (original->current_height());
1416 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1422 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1424 RegionSelection new_views;
1425 PlaylistSet modified_playlists;
1426 RouteTimeAxisView* new_time_axis_view = 0;
1429 /* all changes were made during motion event handlers */
1431 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1435 _editor->commit_reversible_command ();
1439 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1440 PlaylistMapping playlist_mapping;
1442 /* insert the regions into their new playlists */
1443 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1445 RouteTimeAxisView* dest_rtv = 0;
1447 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1453 if (changed_position && !_x_constrained) {
1454 where = i->view->region()->position() - drag_delta;
1456 where = i->view->region()->position();
1459 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1460 /* dragged to drop zone */
1462 PlaylistMapping::iterator pm;
1464 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1465 /* first region from this original playlist: create a new track */
1466 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1467 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1468 dest_rtv = new_time_axis_view;
1470 /* we already created a new track for regions from this playlist, use it */
1471 dest_rtv = pm->second;
1474 /* destination time axis view is the one we dragged to */
1475 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1478 if (dest_rtv != 0) {
1479 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1480 if (new_view != 0) {
1481 new_views.push_back (new_view);
1485 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1486 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1489 list<DraggingView>::const_iterator next = i;
1495 /* If we've created new regions either by copying or moving
1496 to a new track, we want to replace the old selection with the new ones
1499 if (new_views.size() > 0) {
1500 _editor->selection->set (new_views);
1503 /* write commands for the accumulated diffs for all our modified playlists */
1504 add_stateful_diff_commands_for_playlists (modified_playlists);
1506 _editor->commit_reversible_command ();
1510 RegionMoveDrag::finished_no_copy (
1511 bool const changed_position,
1512 bool const changed_tracks,
1513 framecnt_t const drag_delta
1516 RegionSelection new_views;
1517 PlaylistSet modified_playlists;
1518 PlaylistSet frozen_playlists;
1519 set<RouteTimeAxisView*> views_to_update;
1520 RouteTimeAxisView* new_time_axis_view = 0;
1522 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1523 PlaylistMapping playlist_mapping;
1525 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1527 RegionView* rv = i->view;
1528 RouteTimeAxisView* dest_rtv = 0;
1530 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1535 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1536 /* dragged to drop zone */
1538 PlaylistMapping::iterator pm;
1540 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1541 /* first region from this original playlist: create a new track */
1542 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1543 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1544 dest_rtv = new_time_axis_view;
1546 /* we already created a new track for regions from this playlist, use it */
1547 dest_rtv = pm->second;
1551 /* destination time axis view is the one we dragged to */
1552 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1557 double const dest_layer = i->layer;
1559 views_to_update.insert (dest_rtv);
1563 if (changed_position && !_x_constrained) {
1564 where = rv->region()->position() - drag_delta;
1566 where = rv->region()->position();
1569 if (changed_tracks) {
1571 /* insert into new playlist */
1573 RegionView* new_view = insert_region_into_playlist (
1574 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1577 if (new_view == 0) {
1582 new_views.push_back (new_view);
1584 /* remove from old playlist */
1586 /* the region that used to be in the old playlist is not
1587 moved to the new one - we use a copy of it. as a result,
1588 any existing editor for the region should no longer be
1591 rv->hide_region_editor();
1594 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1598 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1600 /* this movement may result in a crossfade being modified, or a layering change,
1601 so we need to get undo data from the playlist as well as the region.
1604 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1606 playlist->clear_changes ();
1609 rv->region()->clear_changes ();
1612 motion on the same track. plonk the previously reparented region
1613 back to its original canvas group (its streamview).
1614 No need to do anything for copies as they are fake regions which will be deleted.
1617 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1618 rv->get_canvas_group()->set_y_position (i->initial_y);
1621 /* just change the model */
1622 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1623 playlist->set_layer (rv->region(), dest_layer);
1626 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1628 r = frozen_playlists.insert (playlist);
1631 playlist->freeze ();
1634 rv->region()->set_position (where);
1635 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1638 if (changed_tracks) {
1640 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1641 was selected in all of them, then removing it from a playlist will have removed all
1642 trace of it from _views (i.e. there were N regions selected, we removed 1,
1643 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1644 corresponding regionview, and _views is now empty).
1646 This could have invalidated any and all iterators into _views.
1648 The heuristic we use here is: if the region selection is empty, break out of the loop
1649 here. if the region selection is not empty, then restart the loop because we know that
1650 we must have removed at least the region(view) we've just been working on as well as any
1651 that we processed on previous iterations.
1653 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1654 we can just iterate.
1658 if (_views.empty()) {
1669 /* If we've created new regions either by copying or moving
1670 to a new track, we want to replace the old selection with the new ones
1673 if (new_views.size() > 0) {
1674 _editor->selection->set (new_views);
1677 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1681 /* write commands for the accumulated diffs for all our modified playlists */
1682 add_stateful_diff_commands_for_playlists (modified_playlists);
1683 /* applies to _brushing */
1684 _editor->commit_reversible_command ();
1686 /* We have futzed with the layering of canvas items on our streamviews.
1687 If any region changed layer, this will have resulted in the stream
1688 views being asked to set up their region views, and all will be well.
1689 If not, we might now have badly-ordered region views. Ask the StreamViews
1690 involved to sort themselves out, just in case.
1693 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1694 (*i)->view()->playlist_layered ((*i)->track ());
1698 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1699 * @param region Region to remove.
1700 * @param playlist playlist To remove from.
1701 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1702 * that clear_changes () is only called once per playlist.
1705 RegionMoveDrag::remove_region_from_playlist (
1706 boost::shared_ptr<Region> region,
1707 boost::shared_ptr<Playlist> playlist,
1708 PlaylistSet& modified_playlists
1711 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1714 playlist->clear_changes ();
1717 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1721 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1722 * clearing the playlist's diff history first if necessary.
1723 * @param region Region to insert.
1724 * @param dest_rtv Destination RouteTimeAxisView.
1725 * @param dest_layer Destination layer.
1726 * @param where Destination position.
1727 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1728 * that clear_changes () is only called once per playlist.
1729 * @return New RegionView, or 0 if no insert was performed.
1732 RegionMoveDrag::insert_region_into_playlist (
1733 boost::shared_ptr<Region> region,
1734 RouteTimeAxisView* dest_rtv,
1737 PlaylistSet& modified_playlists
1740 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1741 if (!dest_playlist) {
1745 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1746 _new_region_view = 0;
1747 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1749 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1750 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1752 dest_playlist->clear_changes ();
1755 dest_playlist->add_region (region, where);
1757 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1758 dest_playlist->set_layer (region, dest_layer);
1763 assert (_new_region_view);
1765 return _new_region_view;
1769 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1771 _new_region_view = rv;
1775 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1777 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1778 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1780 _editor->session()->add_command (c);
1789 RegionMoveDrag::aborted (bool movement_occurred)
1793 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1794 list<DraggingView>::const_iterator next = i;
1803 RegionMotionDrag::aborted (movement_occurred);
1808 RegionMotionDrag::aborted (bool)
1810 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1812 StreamView* sview = (*i)->view();
1815 if (sview->layer_display() == Expanded) {
1816 sview->set_layer_display (Stacked);
1821 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1822 RegionView* rv = i->view;
1823 TimeAxisView* tv = &(rv->get_time_axis_view ());
1824 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1826 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1827 rv->get_canvas_group()->set_y_position (0);
1829 rv->move (-_total_x_delta, 0);
1830 rv->set_height (rtv->view()->child_height ());
1834 /** @param b true to brush, otherwise false.
1835 * @param c true to make copies of the regions being moved, otherwise false.
1837 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1838 : RegionMotionDrag (e, i, p, v, b)
1840 , _new_region_view (0)
1842 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1845 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1846 if (rtv && rtv->is_track()) {
1847 speed = rtv->track()->speed ();
1850 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1854 RegionMoveDrag::setup_pointer_frame_offset ()
1856 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1859 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1860 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1862 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1864 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1865 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1867 _primary = v->view()->create_region_view (r, false, false);
1869 _primary->get_canvas_group()->show ();
1870 _primary->set_position (pos, 0);
1871 _views.push_back (DraggingView (_primary, this, v));
1873 _last_frame_position = pos;
1875 _item = _primary->get_canvas_group ();
1879 RegionInsertDrag::finished (GdkEvent *, bool)
1881 int pos = _views.front().time_axis_view;
1882 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1884 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1886 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1887 _primary->get_canvas_group()->set_y_position (0);
1889 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1891 _editor->begin_reversible_command (Operations::insert_region);
1892 playlist->clear_changes ();
1893 playlist->add_region (_primary->region (), _last_frame_position);
1895 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1896 if (Config->get_edit_mode() == Ripple) {
1897 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1900 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1901 _editor->commit_reversible_command ();
1909 RegionInsertDrag::aborted (bool)
1916 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1917 : RegionMoveDrag (e, i, p, v, false, false)
1919 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1922 struct RegionSelectionByPosition {
1923 bool operator() (RegionView*a, RegionView* b) {
1924 return a->region()->position () < b->region()->position();
1929 RegionSpliceDrag::motion (GdkEvent* event, bool)
1931 /* Which trackview is this ? */
1933 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1934 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1936 /* The region motion is only processed if the pointer is over
1940 if (!tv || !tv->is_track()) {
1941 /* To make sure we hide the verbose canvas cursor when the mouse is
1942 not held over an audio track.
1944 _editor->verbose_cursor()->hide ();
1947 _editor->verbose_cursor()->show ();
1952 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1958 RegionSelection copy;
1959 _editor->selection->regions.by_position(copy);
1961 framepos_t const pf = adjusted_current_frame (event);
1963 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1965 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1971 boost::shared_ptr<Playlist> playlist;
1973 if ((playlist = atv->playlist()) == 0) {
1977 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1982 if (pf < (*i)->region()->last_frame() + 1) {
1986 if (pf > (*i)->region()->first_frame()) {
1992 playlist->shuffle ((*i)->region(), dir);
1997 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1999 RegionMoveDrag::finished (event, movement_occurred);
2003 RegionSpliceDrag::aborted (bool)
2013 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2016 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2018 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2019 RegionSelection to_ripple;
2020 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2021 if ((*i)->position() >= where) {
2022 to_ripple.push_back (rtv->view()->find_view(*i));
2026 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2027 if (!exclude.contains (*i)) {
2028 // the selection has already been added to _views
2030 if (drag_in_progress) {
2031 // do the same things that RegionMotionDrag::motion does when
2032 // first_move is true, for the region views that we're adding
2033 // to _views this time
2036 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2037 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2038 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2039 rvg->reparent (_editor->_drag_motion_group);
2041 // we only need to move in the y direction
2042 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2047 _views.push_back (DraggingView (*i, this, tav));
2053 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2056 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2057 // we added all the regions after the selection
2059 std::list<DraggingView>::iterator to_erase = i++;
2060 if (!_editor->selection->regions.contains (to_erase->view)) {
2061 // restore the non-selected regions to their original playlist & positions,
2062 // and then ripple them back by the length of the regions that were dragged away
2063 // do the same things as RegionMotionDrag::aborted
2065 RegionView *rv = to_erase->view;
2066 TimeAxisView* tv = &(rv->get_time_axis_view ());
2067 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2070 // plonk them back onto their own track
2071 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2072 rv->get_canvas_group()->set_y_position (0);
2076 // move the underlying region to match the view
2077 rv->region()->set_position (rv->region()->position() + amount);
2079 // restore the view to match the underlying region's original position
2080 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2083 rv->set_height (rtv->view()->child_height ());
2084 _views.erase (to_erase);
2090 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2092 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2094 return allow_moves_across_tracks;
2102 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2103 : RegionMoveDrag (e, i, p, v, false, false)
2105 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2106 // compute length of selection
2107 RegionSelection selected_regions = _editor->selection->regions;
2108 selection_length = selected_regions.end_frame() - selected_regions.start();
2110 // we'll only allow dragging to another track in ripple mode if all the regions
2111 // being dragged start off on the same track
2112 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2115 exclude = new RegionList;
2116 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2117 exclude->push_back((*i)->region());
2120 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2121 RegionSelection copy;
2122 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2124 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2125 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2127 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2128 // find ripple start point on each applicable playlist
2129 RegionView *first_selected_on_this_track = NULL;
2130 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2131 if ((*i)->region()->playlist() == (*pi)) {
2132 // region is on this playlist - it's the first, because they're sorted
2133 first_selected_on_this_track = *i;
2137 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2138 add_all_after_to_views (
2139 &first_selected_on_this_track->get_time_axis_view(),
2140 first_selected_on_this_track->region()->position(),
2141 selected_regions, false);
2144 if (allow_moves_across_tracks) {
2145 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2153 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2155 /* Which trackview is this ? */
2157 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2158 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2160 /* The region motion is only processed if the pointer is over
2164 if (!tv || !tv->is_track()) {
2165 /* To make sure we hide the verbose canvas cursor when the mouse is
2166 not held over an audiotrack.
2168 _editor->verbose_cursor()->hide ();
2172 framepos_t where = adjusted_current_frame (event);
2173 assert (where >= 0);
2175 double delta = compute_x_delta (event, &after);
2177 framecnt_t amount = _editor->pixel_to_sample (delta);
2179 if (allow_moves_across_tracks) {
2180 // all the originally selected regions were on the same track
2182 framecnt_t adjust = 0;
2183 if (prev_tav && tv != prev_tav) {
2184 // dragged onto a different track
2185 // remove the unselected regions from _views, restore them to their original positions
2186 // and add the regions after the drop point on the new playlist to _views instead.
2187 // undo the effect of rippling the previous playlist, and include the effect of removing
2188 // the dragged region(s) from this track
2190 remove_unselected_from_views (prev_amount, false);
2191 // ripple previous playlist according to the regions that have been removed onto the new playlist
2192 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2195 // move just the selected regions
2196 RegionMoveDrag::motion(event, first_move);
2198 // ensure that the ripple operation on the new playlist inserts selection_length time
2199 adjust = selection_length;
2200 // ripple the new current playlist
2201 tv->playlist()->ripple (where, amount+adjust, exclude);
2203 // add regions after point where drag entered this track to subsequent ripples
2204 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2207 // motion on same track
2208 RegionMoveDrag::motion(event, first_move);
2212 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2213 prev_position = where;
2215 // selection encompasses multiple tracks - just drag
2216 // cross-track drags are forbidden
2217 RegionMoveDrag::motion(event, first_move);
2220 if (!_x_constrained) {
2221 prev_amount += amount;
2224 _last_frame_position = after;
2228 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2230 if (!movement_occurred) {
2234 if (was_double_click() && !_views.empty()) {
2235 DraggingView dv = _views.front();
2236 dv.view->show_region_editor ();
2243 _editor->begin_reversible_command(_("Ripple drag"));
2245 // remove the regions being rippled from the dragging view, updating them to
2246 // their new positions
2247 remove_unselected_from_views (prev_amount, true);
2249 if (allow_moves_across_tracks) {
2251 // if regions were dragged across tracks, we've rippled any later
2252 // regions on the track the regions were dragged off, so we need
2253 // to add the original track to the undo record
2254 orig_tav->playlist()->clear_changes();
2255 vector<Command*> cmds;
2256 orig_tav->playlist()->rdiff (cmds);
2257 _editor->session()->add_commands (cmds);
2259 if (prev_tav && prev_tav != orig_tav) {
2260 prev_tav->playlist()->clear_changes();
2261 vector<Command*> cmds;
2262 prev_tav->playlist()->rdiff (cmds);
2263 _editor->session()->add_commands (cmds);
2266 // selection spanned multiple tracks - all will need adding to undo record
2268 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2269 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2271 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2272 (*pi)->clear_changes();
2273 vector<Command*> cmds;
2274 (*pi)->rdiff (cmds);
2275 _editor->session()->add_commands (cmds);
2279 // other modified playlists are added to undo by RegionMoveDrag::finished()
2280 RegionMoveDrag::finished (event, movement_occurred);
2281 _editor->commit_reversible_command();
2285 RegionRippleDrag::aborted (bool movement_occurred)
2287 RegionMoveDrag::aborted (movement_occurred);
2292 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2294 _view (dynamic_cast<MidiTimeAxisView*> (v))
2296 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2302 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2305 _editor->begin_reversible_command (_("create region"));
2306 _region = add_midi_region (_view, false);
2307 _view->playlist()->freeze ();
2310 framepos_t const f = adjusted_current_frame (event);
2311 if (f < grab_frame()) {
2312 _region->set_initial_position (f);
2315 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2316 so that if this region is duplicated, its duplicate starts on
2317 a snap point rather than 1 frame after a snap point. Otherwise things get
2318 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2319 place snapped notes at the start of the region.
2322 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2323 _region->set_length (len < 1 ? 1 : len);
2329 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2331 if (!movement_occurred) {
2332 add_midi_region (_view, true);
2334 _view->playlist()->thaw ();
2335 _editor->commit_reversible_command();
2340 RegionCreateDrag::aborted (bool)
2343 _view->playlist()->thaw ();
2349 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2354 , _was_selected (false)
2357 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2361 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2363 Gdk::Cursor* cursor;
2364 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2366 float x_fraction = cnote->mouse_x_fraction ();
2368 if (x_fraction > 0.0 && x_fraction < 0.25) {
2369 cursor = _editor->cursors()->left_side_trim;
2372 cursor = _editor->cursors()->right_side_trim;
2376 Drag::start_grab (event, cursor);
2378 region = &cnote->region_view();
2381 temp = region->snap_to_pixel (cnote->x0 (), true);
2382 _snap_delta = temp - cnote->x0 ();
2386 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2391 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2392 if (ms.size() > 1) {
2393 /* has to be relative, may make no sense otherwise */
2397 if (!(_was_selected = cnote->selected())) {
2399 /* tertiary-click means extend selection - we'll do that on button release,
2400 so don't add it here, because otherwise we make it hard to figure
2401 out the "extend-to" range.
2404 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2407 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2410 region->note_selected (cnote, true);
2412 _editor->get_selection().clear_points();
2413 region->unique_select (cnote);
2420 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2422 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2424 _editor->begin_reversible_command (_("resize notes"));
2426 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2427 MidiRegionSelection::iterator next;
2430 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2432 mrv->begin_resizing (at_front);
2438 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2439 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2441 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2445 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2447 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2448 if (_editor->snap_mode () != SnapOff) {
2452 if (_editor->snap_mode () == SnapOff) {
2454 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2455 if (apply_snap_delta) {
2461 if (apply_snap_delta) {
2465 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2471 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2473 if (!movement_occurred) {
2474 /* no motion - select note */
2475 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2476 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2477 _editor->current_mouse_mode() == Editing::MouseDraw) {
2479 bool changed = false;
2481 if (_was_selected) {
2482 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2484 region->note_deselected (cnote);
2487 _editor->get_selection().clear_points();
2488 region->unique_select (cnote);
2492 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2493 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2495 if (!extend && !add && region->selection_size() > 1) {
2496 _editor->get_selection().clear_points();
2497 region->unique_select (cnote);
2499 } else if (extend) {
2500 region->note_selected (cnote, true, true);
2503 /* it was added during button press */
2509 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2510 _editor->commit_reversible_selection_op();
2517 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2518 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2519 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2521 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2524 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2526 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2527 if (_editor->snap_mode () != SnapOff) {
2531 if (_editor->snap_mode () == SnapOff) {
2533 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2534 if (apply_snap_delta) {
2540 if (apply_snap_delta) {
2544 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2548 _editor->commit_reversible_command ();
2552 NoteResizeDrag::aborted (bool)
2554 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2555 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2556 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2558 mrv->abort_resizing ();
2563 AVDraggingView::AVDraggingView (RegionView* v)
2566 initial_position = v->region()->position ();
2569 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2572 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2575 TrackViewList empty;
2577 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2578 std::list<RegionView*> views = rs.by_layer();
2581 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2582 RegionView* rv = (*i);
2583 if (!rv->region()->video_locked()) {
2586 if (rv->region()->locked()) {
2589 _views.push_back (AVDraggingView (rv));
2594 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2596 Drag::start_grab (event);
2597 if (_editor->session() == 0) {
2601 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2607 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2611 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2612 _max_backwards_drag = (
2613 ARDOUR_UI::instance()->video_timeline->get_duration()
2614 + ARDOUR_UI::instance()->video_timeline->get_offset()
2615 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2618 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2619 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2620 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2623 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2626 Timecode::Time timecode;
2627 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2628 snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2629 show_verbose_cursor_text (buf);
2633 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2635 if (_editor->session() == 0) {
2638 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2642 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2646 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2647 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2649 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2650 dt = - _max_backwards_drag;
2653 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2654 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2656 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2657 RegionView* rv = i->view;
2658 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2661 rv->region()->clear_changes ();
2662 rv->region()->suspend_property_changes();
2664 rv->region()->set_position(i->initial_position + dt);
2665 rv->region_changed(ARDOUR::Properties::position);
2668 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2669 Timecode::Time timecode;
2670 Timecode::Time timediff;
2672 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2673 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2674 snprintf (buf, sizeof (buf),
2675 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2676 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2677 , _("Video Start:"),
2678 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2680 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2682 show_verbose_cursor_text (buf);
2686 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2688 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2695 if (!movement_occurred || ! _editor->session()) {
2699 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2701 _editor->begin_reversible_command (_("Move Video"));
2703 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2704 ARDOUR_UI::instance()->video_timeline->save_undo();
2705 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2706 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2708 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2709 i->view->drag_end();
2710 i->view->region()->resume_property_changes ();
2712 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2715 _editor->session()->maybe_update_session_range(
2716 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2717 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2721 _editor->commit_reversible_command ();
2725 VideoTimeLineDrag::aborted (bool)
2727 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2730 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2731 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2733 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2734 i->view->region()->resume_property_changes ();
2735 i->view->region()->set_position(i->initial_position);
2739 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2740 : RegionDrag (e, i, p, v)
2741 , _operation (StartTrim)
2742 , _preserve_fade_anchor (preserve_fade_anchor)
2743 , _jump_position_when_done (false)
2745 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2749 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2752 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2753 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2755 if (tv && tv->is_track()) {
2756 speed = tv->track()->speed();
2759 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2760 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2761 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2763 framepos_t const pf = adjusted_current_frame (event);
2764 setup_snap_delta (region_start);
2766 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2767 /* Move the contents of the region around without changing the region bounds */
2768 _operation = ContentsTrim;
2769 Drag::start_grab (event, _editor->cursors()->trimmer);
2771 /* These will get overridden for a point trim.*/
2772 if (pf < (region_start + region_length/2)) {
2773 /* closer to front */
2774 _operation = StartTrim;
2775 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2776 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2778 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2782 _operation = EndTrim;
2783 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2784 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2786 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2790 /* jump trim disabled for now
2791 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2792 _jump_position_when_done = true;
2796 switch (_operation) {
2798 show_verbose_cursor_time (region_start);
2801 show_verbose_cursor_duration (region_start, region_end);
2804 show_verbose_cursor_time (pf);
2808 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2809 i->view->region()->suspend_property_changes ();
2814 TrimDrag::motion (GdkEvent* event, bool first_move)
2816 RegionView* rv = _primary;
2819 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2820 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2821 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2822 frameoffset_t frame_delta = 0;
2824 if (tv && tv->is_track()) {
2825 speed = tv->track()->speed();
2827 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2828 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2834 switch (_operation) {
2836 trim_type = "Region start trim";
2839 trim_type = "Region end trim";
2842 trim_type = "Region content trim";
2849 _editor->begin_reversible_command (trim_type);
2851 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2852 RegionView* rv = i->view;
2853 rv->enable_display (false);
2854 rv->region()->playlist()->clear_owned_changes ();
2856 if (_operation == StartTrim) {
2857 rv->trim_front_starting ();
2860 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2863 arv->temporarily_hide_envelope ();
2867 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2868 insert_result = _editor->motion_frozen_playlists.insert (pl);
2870 if (insert_result.second) {
2876 bool non_overlap_trim = false;
2878 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2879 non_overlap_trim = true;
2882 /* contstrain trim to fade length */
2883 if (_preserve_fade_anchor) {
2884 switch (_operation) {
2886 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2887 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2889 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2890 if (ar->locked()) continue;
2891 framecnt_t len = ar->fade_in()->back()->when;
2892 if (len < dt) dt = min(dt, len);
2896 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2897 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2899 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2900 if (ar->locked()) continue;
2901 framecnt_t len = ar->fade_out()->back()->when;
2902 if (len < -dt) dt = max(dt, -len);
2911 switch (_operation) {
2913 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2914 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2915 if (changed && _preserve_fade_anchor) {
2916 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2918 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2919 framecnt_t len = ar->fade_in()->back()->when;
2920 framecnt_t diff = ar->first_frame() - i->initial_position;
2921 framepos_t new_length = len - diff;
2922 i->anchored_fade_length = min (ar->length(), new_length);
2923 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2924 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2931 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2932 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2933 if (changed && _preserve_fade_anchor) {
2934 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2936 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2937 framecnt_t len = ar->fade_out()->back()->when;
2938 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2939 framepos_t new_length = len + diff;
2940 i->anchored_fade_length = min (ar->length(), new_length);
2941 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2942 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2950 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2952 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2953 i->view->move_contents (frame_delta);
2959 switch (_operation) {
2961 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2964 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2967 // show_verbose_cursor_time (frame_delta);
2973 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2975 if (movement_occurred) {
2976 motion (event, false);
2978 if (_operation == StartTrim) {
2979 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2981 /* This must happen before the region's StatefulDiffCommand is created, as it may
2982 `correct' (ahem) the region's _start from being negative to being zero. It
2983 needs to be zero in the undo record.
2985 i->view->trim_front_ending ();
2987 if (_preserve_fade_anchor) {
2988 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2990 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2991 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2992 ar->set_fade_in_length(i->anchored_fade_length);
2993 ar->set_fade_in_active(true);
2996 if (_jump_position_when_done) {
2997 i->view->region()->set_position (i->initial_position);
3000 } else if (_operation == EndTrim) {
3001 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3002 if (_preserve_fade_anchor) {
3003 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3005 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3006 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3007 ar->set_fade_out_length(i->anchored_fade_length);
3008 ar->set_fade_out_active(true);
3011 if (_jump_position_when_done) {
3012 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3017 if (!_views.empty()) {
3018 if (_operation == StartTrim) {
3019 _editor->maybe_locate_with_edit_preroll(
3020 _views.begin()->view->region()->position());
3022 if (_operation == EndTrim) {
3023 _editor->maybe_locate_with_edit_preroll(
3024 _views.begin()->view->region()->position() +
3025 _views.begin()->view->region()->length());
3029 if (!_editor->selection->selected (_primary)) {
3030 _primary->thaw_after_trim ();
3033 set<boost::shared_ptr<Playlist> > diffed_playlists;
3035 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3036 i->view->thaw_after_trim ();
3037 i->view->enable_display (true);
3039 /* Trimming one region may affect others on the playlist, so we need
3040 to get undo Commands from the whole playlist rather than just the
3041 region. Use diffed_playlists to make sure we don't diff a given
3042 playlist more than once.
3044 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3045 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3046 vector<Command*> cmds;
3048 _editor->session()->add_commands (cmds);
3049 diffed_playlists.insert (p);
3054 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3058 _editor->motion_frozen_playlists.clear ();
3059 _editor->commit_reversible_command();
3062 /* no mouse movement */
3063 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3064 _editor->point_trim (event, adjusted_current_frame (event));
3068 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3069 i->view->region()->resume_property_changes ();
3074 TrimDrag::aborted (bool movement_occurred)
3076 /* Our motion method is changing model state, so use the Undo system
3077 to cancel. Perhaps not ideal, as this will leave an Undo point
3078 behind which may be slightly odd from the user's point of view.
3083 if (movement_occurred) {
3087 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3088 i->view->region()->resume_property_changes ();
3093 TrimDrag::setup_pointer_frame_offset ()
3095 list<DraggingView>::iterator i = _views.begin ();
3096 while (i != _views.end() && i->view != _primary) {
3100 if (i == _views.end()) {
3104 switch (_operation) {
3106 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3109 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3116 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3119 , _old_snap_type (e->snap_type())
3120 , _old_snap_mode (e->snap_mode())
3123 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3124 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3126 _real_section = &_marker->meter();
3131 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3133 Drag::start_grab (event, cursor);
3134 show_verbose_cursor_time (adjusted_current_frame(event));
3138 MeterMarkerDrag::setup_pointer_frame_offset ()
3140 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3144 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3147 // create a dummy marker to catch events, then hide it.
3150 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3152 _marker = new MeterMarker (
3154 *_editor->meter_group,
3155 UIConfiguration::instance().color ("meter marker"),
3157 *new MeterSection (_marker->meter())
3160 /* use the new marker for the grab */
3161 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3164 TempoMap& map (_editor->session()->tempo_map());
3165 /* get current state */
3166 before_state = &map.get_state();
3169 _editor->begin_reversible_command (_("move meter mark"));
3171 _editor->begin_reversible_command (_("copy meter mark"));
3173 Timecode::BBT_Time bbt = _real_section->bbt();
3175 /* we can't add a meter where one currently exists */
3176 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3181 const double beat = map.beat_at_bbt (bbt);
3182 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3183 , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3186 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3187 if (_real_section->position_lock_style() != AudioTime) {
3188 _editor->set_snap_to (SnapToBar);
3189 _editor->set_snap_mode (SnapNormal);
3193 framepos_t pf = adjusted_current_frame (event);
3195 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3196 /* never snap to music for audio locked */
3197 pf = adjusted_current_frame (event, false);
3200 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3202 /* fake marker meeds to stay under the mouse, unlike the real one. */
3203 _marker->set_position (adjusted_current_frame (event, false));
3205 show_verbose_cursor_time (_real_section->frame());
3209 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3211 if (!movement_occurred) {
3212 if (was_double_click()) {
3213 _editor->edit_meter_marker (*_marker);
3218 /* reinstate old snap setting */
3219 _editor->set_snap_to (_old_snap_type);
3220 _editor->set_snap_mode (_old_snap_mode);
3222 TempoMap& map (_editor->session()->tempo_map());
3224 XMLNode &after = map.get_state();
3225 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3226 _editor->commit_reversible_command ();
3228 // delete the dummy marker we used for visual representation while moving.
3229 // a new visual marker will show up automatically.
3234 MeterMarkerDrag::aborted (bool moved)
3236 _marker->set_position (_marker->meter().frame ());
3238 /* reinstate old snap setting */
3239 _editor->set_snap_to (_old_snap_type);
3240 _editor->set_snap_mode (_old_snap_mode);
3242 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3243 // delete the dummy marker we used for visual representation while moving.
3244 // a new visual marker will show up automatically.
3249 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3254 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3256 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3257 _real_section = &_marker->tempo();
3258 _movable = _real_section->movable();
3263 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3265 Drag::start_grab (event, cursor);
3266 if (!_real_section->active()) {
3267 show_verbose_cursor_text (_("inactive"));
3269 show_verbose_cursor_time (adjusted_current_frame (event));
3274 TempoMarkerDrag::setup_pointer_frame_offset ()
3276 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3280 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3282 if (!_real_section->active()) {
3288 // mvc drag - create a dummy marker to catch events, hide it.
3291 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3293 TempoSection section (_marker->tempo());
3295 _marker = new TempoMarker (
3297 *_editor->tempo_group,
3298 UIConfiguration::instance().color ("tempo marker"),
3300 *new TempoSection (_marker->tempo())
3303 /* use the new marker for the grab */
3304 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3307 TempoMap& map (_editor->session()->tempo_map());
3308 /* get current state */
3309 before_state = &map.get_state();
3312 _editor->begin_reversible_command (_("move tempo mark"));
3315 const framepos_t frame = adjusted_current_frame (event) + 1;
3317 _editor->begin_reversible_command (_("copy tempo mark"));
3319 if (_real_section->position_lock_style() == MusicTime) {
3320 _real_section = map.add_tempo (_marker->tempo(), map.pulse_at_frame (frame), 0, _real_section->type(), MusicTime);
3322 _real_section = map.add_tempo (_marker->tempo(), 0.0, frame, _real_section->type(), AudioTime);
3328 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) {
3329 /* use vertical movement to alter tempo .. should be log */
3330 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3333 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3335 show_verbose_cursor_text (strs.str());
3337 } else if (_movable && !_real_section->locked_to_meter()) {
3338 const framepos_t pf = adjusted_current_frame (event);
3339 TempoMap& map (_editor->session()->tempo_map());
3341 /* snap to beat is 1, snap to bar is -1 (sorry) */
3342 int sub_num = _editor->get_grid_music_divisions (event->button.state);
3344 map.gui_move_tempo (_real_section, pf, sub_num);
3346 show_verbose_cursor_time (_real_section->frame());
3348 _marker->set_position (adjusted_current_frame (event, false));
3352 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3354 if (!_real_section->active()) {
3357 if (!movement_occurred) {
3358 if (was_double_click()) {
3359 _editor->edit_tempo_marker (*_marker);
3364 TempoMap& map (_editor->session()->tempo_map());
3366 XMLNode &after = map.get_state();
3367 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3368 _editor->commit_reversible_command ();
3370 // delete the dummy marker we used for visual representation while moving.
3371 // a new visual marker will show up automatically.
3376 TempoMarkerDrag::aborted (bool moved)
3378 _marker->set_position (_marker->tempo().frame());
3380 TempoMap& map (_editor->session()->tempo_map());
3381 map.set_state (*before_state, Stateful::current_state_version);
3382 // delete the dummy marker we used for visual representation while moving.
3383 // a new visual marker will show up automatically.
3388 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3394 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3399 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3401 Drag::start_grab (event, cursor);
3402 TempoMap& map (_editor->session()->tempo_map());
3403 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3406 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3407 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3408 show_verbose_cursor_text (sstr.str());
3409 finished (event, false);
3413 BBTRulerDrag::setup_pointer_frame_offset ()
3415 TempoMap& map (_editor->session()->tempo_map());
3416 const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3417 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3420 if (divisions > 0) {
3421 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3423 /* while it makes some sense for the user to determine the division to 'grab',
3424 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3425 and the result over steep tempo curves. Use sixteenths.
3427 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3430 _pulse = map.pulse_at_beat (beat);
3432 _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3437 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3439 TempoMap& map (_editor->session()->tempo_map());
3442 /* get current state */
3443 before_state = &map.get_state();
3444 _editor->begin_reversible_command (_("dilate tempo"));
3449 if (_editor->snap_musical()) {
3450 pf = adjusted_current_frame (event, false);
3452 pf = adjusted_current_frame (event);
3455 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3456 /* adjust previous tempo to match pointer frame */
3457 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3460 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3461 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3462 show_verbose_cursor_text (sstr.str());
3466 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3468 if (!movement_occurred) {
3472 TempoMap& map (_editor->session()->tempo_map());
3474 XMLNode &after = map.get_state();
3475 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3476 _editor->commit_reversible_command ();
3480 BBTRulerDrag::aborted (bool moved)
3483 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3488 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3489 : Drag (e, &c.track_canvas_item(), false)
3494 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3497 /** Do all the things we do when dragging the playhead to make it look as though
3498 * we have located, without actually doing the locate (because that would cause
3499 * the diskstream buffers to be refilled, which is too slow).
3502 CursorDrag::fake_locate (framepos_t t)
3504 if (_editor->session () == 0) {
3508 _editor->playhead_cursor->set_position (t);
3510 Session* s = _editor->session ();
3511 if (s->timecode_transmission_suspended ()) {
3512 framepos_t const f = _editor->playhead_cursor->current_frame ();
3513 /* This is asynchronous so it will be sent "now"
3515 s->send_mmc_locate (f);
3516 /* These are synchronous and will be sent during the next
3519 s->queue_full_time_code ();
3520 s->queue_song_position_pointer ();
3523 show_verbose_cursor_time (t);
3524 _editor->UpdateAllTransportClocks (t);
3528 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3530 Drag::start_grab (event, c);
3531 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3533 _grab_zoom = _editor->samples_per_pixel;
3535 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3537 _editor->snap_to_with_modifier (where, event);
3539 _editor->_dragging_playhead = true;
3541 Session* s = _editor->session ();
3543 /* grab the track canvas item as well */
3545 _cursor.track_canvas_item().grab();
3548 if (_was_rolling && _stop) {
3552 if (s->is_auditioning()) {
3553 s->cancel_audition ();
3557 if (AudioEngine::instance()->connected()) {
3559 /* do this only if we're the engine is connected
3560 * because otherwise this request will never be
3561 * serviced and we'll busy wait forever. likewise,
3562 * notice if we are disconnected while waiting for the
3563 * request to be serviced.
3566 s->request_suspend_timecode_transmission ();
3567 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3568 /* twiddle our thumbs */
3573 fake_locate (where - snap_delta (event->button.state));
3577 CursorDrag::motion (GdkEvent* event, bool)
3579 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3580 _editor->snap_to_with_modifier (where, event);
3581 if (where != last_pointer_frame()) {
3582 fake_locate (where - snap_delta (event->button.state));
3587 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3589 _editor->_dragging_playhead = false;
3591 _cursor.track_canvas_item().ungrab();
3593 if (!movement_occurred && _stop) {
3597 motion (event, false);
3599 Session* s = _editor->session ();
3601 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3602 _editor->_pending_locate_request = true;
3603 s->request_resume_timecode_transmission ();
3608 CursorDrag::aborted (bool)
3610 _cursor.track_canvas_item().ungrab();
3612 if (_editor->_dragging_playhead) {
3613 _editor->session()->request_resume_timecode_transmission ();
3614 _editor->_dragging_playhead = false;
3617 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3620 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3621 : RegionDrag (e, i, p, v)
3623 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3627 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3629 Drag::start_grab (event, cursor);
3631 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3632 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3633 setup_snap_delta (r->position ());
3635 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3639 FadeInDrag::setup_pointer_frame_offset ()
3641 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3642 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3643 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3647 FadeInDrag::motion (GdkEvent* event, bool)
3649 framecnt_t fade_length;
3651 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3652 _editor->snap_to_with_modifier (pos, event);
3653 pos -= snap_delta (event->button.state);
3655 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3657 if (pos < (region->position() + 64)) {
3658 fade_length = 64; // this should be a minimum defined somewhere
3659 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3660 fade_length = region->length() - region->fade_out()->back()->when - 1;
3662 fade_length = pos - region->position();
3665 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3667 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3673 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3676 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3680 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3682 if (!movement_occurred) {
3686 framecnt_t fade_length;
3687 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3688 _editor->snap_to_with_modifier (pos, event);
3689 pos -= snap_delta (event->button.state);
3691 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3693 if (pos < (region->position() + 64)) {
3694 fade_length = 64; // this should be a minimum defined somewhere
3695 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3696 fade_length = region->length() - region->fade_out()->back()->when - 1;
3698 fade_length = pos - region->position();
3701 bool in_command = false;
3703 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3705 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3711 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3712 XMLNode &before = alist->get_state();
3714 tmp->audio_region()->set_fade_in_length (fade_length);
3715 tmp->audio_region()->set_fade_in_active (true);
3718 _editor->begin_reversible_command (_("change fade in length"));
3721 XMLNode &after = alist->get_state();
3722 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3726 _editor->commit_reversible_command ();
3731 FadeInDrag::aborted (bool)
3733 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3734 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3740 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3744 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3745 : RegionDrag (e, i, p, v)
3747 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3751 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3753 Drag::start_grab (event, cursor);
3755 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3756 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3757 setup_snap_delta (r->last_frame ());
3759 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3763 FadeOutDrag::setup_pointer_frame_offset ()
3765 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3766 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3767 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3771 FadeOutDrag::motion (GdkEvent* event, bool)
3773 framecnt_t fade_length;
3775 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3776 _editor->snap_to_with_modifier (pos, event);
3777 pos -= snap_delta (event->button.state);
3779 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3781 if (pos > (region->last_frame() - 64)) {
3782 fade_length = 64; // this should really be a minimum fade defined somewhere
3783 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3784 fade_length = region->length() - region->fade_in()->back()->when - 1;
3786 fade_length = region->last_frame() - pos;
3789 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3791 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3797 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3800 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3804 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3806 if (!movement_occurred) {
3810 framecnt_t fade_length;
3812 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3813 _editor->snap_to_with_modifier (pos, event);
3814 pos -= snap_delta (event->button.state);
3816 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3818 if (pos > (region->last_frame() - 64)) {
3819 fade_length = 64; // this should really be a minimum fade defined somewhere
3820 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3821 fade_length = region->length() - region->fade_in()->back()->when - 1;
3823 fade_length = region->last_frame() - pos;
3826 bool in_command = false;
3828 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3830 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3836 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3837 XMLNode &before = alist->get_state();
3839 tmp->audio_region()->set_fade_out_length (fade_length);
3840 tmp->audio_region()->set_fade_out_active (true);
3843 _editor->begin_reversible_command (_("change fade out length"));
3846 XMLNode &after = alist->get_state();
3847 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3851 _editor->commit_reversible_command ();
3856 FadeOutDrag::aborted (bool)
3858 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3859 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3865 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3869 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3871 , _selection_changed (false)
3873 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3874 Gtk::Window* toplevel = _editor->current_toplevel();
3875 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3879 _points.push_back (ArdourCanvas::Duple (0, 0));
3881 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3884 MarkerDrag::~MarkerDrag ()
3886 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3891 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3893 location = new Location (*l);
3894 markers.push_back (m);
3899 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3901 Drag::start_grab (event, cursor);
3905 Location *location = _editor->find_location_from_marker (_marker, is_start);
3906 _editor->_dragging_edit_point = true;
3908 update_item (location);
3910 // _drag_line->show();
3911 // _line->raise_to_top();
3914 show_verbose_cursor_time (location->start());
3916 show_verbose_cursor_time (location->end());
3918 setup_snap_delta (is_start ? location->start() : location->end());
3920 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3923 case Selection::Toggle:
3924 /* we toggle on the button release */
3926 case Selection::Set:
3927 if (!_editor->selection->selected (_marker)) {
3928 _editor->selection->set (_marker);
3929 _selection_changed = true;
3932 case Selection::Extend:
3934 Locations::LocationList ll;
3935 list<ArdourMarker*> to_add;
3937 _editor->selection->markers.range (s, e);
3938 s = min (_marker->position(), s);
3939 e = max (_marker->position(), e);
3942 if (e < max_framepos) {
3945 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3946 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3947 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3950 to_add.push_back (lm->start);
3953 to_add.push_back (lm->end);
3957 if (!to_add.empty()) {
3958 _editor->selection->add (to_add);
3959 _selection_changed = true;
3963 case Selection::Add:
3964 _editor->selection->add (_marker);
3965 _selection_changed = true;
3970 /* Set up copies for us to manipulate during the drag
3973 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3975 Location* l = _editor->find_location_from_marker (*i, is_start);
3982 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3984 /* range: check that the other end of the range isn't
3987 CopiedLocationInfo::iterator x;
3988 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3989 if (*(*x).location == *l) {
3993 if (x == _copied_locations.end()) {
3994 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3996 (*x).markers.push_back (*i);
3997 (*x).move_both = true;
4005 MarkerDrag::setup_pointer_frame_offset ()
4008 Location *location = _editor->find_location_from_marker (_marker, is_start);
4009 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4013 MarkerDrag::motion (GdkEvent* event, bool)
4015 framecnt_t f_delta = 0;
4017 bool move_both = false;
4018 Location *real_location;
4019 Location *copy_location = 0;
4020 framecnt_t const sd = snap_delta (event->button.state);
4022 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4023 framepos_t next = newframe;
4025 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4029 CopiedLocationInfo::iterator x;
4031 /* find the marker we're dragging, and compute the delta */
4033 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4035 copy_location = (*x).location;
4037 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4039 /* this marker is represented by this
4040 * CopiedLocationMarkerInfo
4043 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4048 if (real_location->is_mark()) {
4049 f_delta = newframe - copy_location->start();
4053 switch (_marker->type()) {
4054 case ArdourMarker::SessionStart:
4055 case ArdourMarker::RangeStart:
4056 case ArdourMarker::LoopStart:
4057 case ArdourMarker::PunchIn:
4058 f_delta = newframe - copy_location->start();
4061 case ArdourMarker::SessionEnd:
4062 case ArdourMarker::RangeEnd:
4063 case ArdourMarker::LoopEnd:
4064 case ArdourMarker::PunchOut:
4065 f_delta = newframe - copy_location->end();
4068 /* what kind of marker is this ? */
4077 if (x == _copied_locations.end()) {
4078 /* hmm, impossible - we didn't find the dragged marker */
4082 /* now move them all */
4084 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4086 copy_location = x->location;
4088 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4092 if (real_location->locked()) {
4096 if (copy_location->is_mark()) {
4100 copy_location->set_start (copy_location->start() + f_delta);
4104 framepos_t new_start = copy_location->start() + f_delta;
4105 framepos_t new_end = copy_location->end() + f_delta;
4107 if (is_start) { // start-of-range marker
4109 if (move_both || (*x).move_both) {
4110 copy_location->set_start (new_start);
4111 copy_location->set_end (new_end);
4112 } else if (new_start < copy_location->end()) {
4113 copy_location->set_start (new_start);
4114 } else if (newframe > 0) {
4115 //_editor->snap_to (next, RoundUpAlways, true);
4116 copy_location->set_end (next);
4117 copy_location->set_start (newframe);
4120 } else { // end marker
4122 if (move_both || (*x).move_both) {
4123 copy_location->set_end (new_end);
4124 copy_location->set_start (new_start);
4125 } else if (new_end > copy_location->start()) {
4126 copy_location->set_end (new_end);
4127 } else if (newframe > 0) {
4128 //_editor->snap_to (next, RoundDownAlways, true);
4129 copy_location->set_start (next);
4130 copy_location->set_end (newframe);
4135 update_item (copy_location);
4137 /* now lookup the actual GUI items used to display this
4138 * location and move them to wherever the copy of the location
4139 * is now. This means that the logic in ARDOUR::Location is
4140 * still enforced, even though we are not (yet) modifying
4141 * the real Location itself.
4144 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4147 lm->set_position (copy_location->start(), copy_location->end());
4152 assert (!_copied_locations.empty());
4154 show_verbose_cursor_time (newframe);
4158 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4160 if (!movement_occurred) {
4162 if (was_double_click()) {
4163 _editor->rename_marker (_marker);
4167 /* just a click, do nothing but finish
4168 off the selection process
4171 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4173 case Selection::Set:
4174 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4175 _editor->selection->set (_marker);
4176 _selection_changed = true;
4180 case Selection::Toggle:
4181 /* we toggle on the button release, click only */
4182 _editor->selection->toggle (_marker);
4183 _selection_changed = true;
4187 case Selection::Extend:
4188 case Selection::Add:
4192 if (_selection_changed) {
4193 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4194 _editor->commit_reversible_selection_op();
4200 _editor->_dragging_edit_point = false;
4202 XMLNode &before = _editor->session()->locations()->get_state();
4203 bool in_command = false;
4205 MarkerSelection::iterator i;
4206 CopiedLocationInfo::iterator x;
4209 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4210 x != _copied_locations.end() && i != _editor->selection->markers.end();
4213 Location * location = _editor->find_location_from_marker (*i, is_start);
4217 if (location->locked()) {
4221 _editor->begin_reversible_command ( _("move marker") );
4224 if (location->is_mark()) {
4225 location->set_start (((*x).location)->start());
4227 location->set (((*x).location)->start(), ((*x).location)->end());
4233 XMLNode &after = _editor->session()->locations()->get_state();
4234 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4235 _editor->commit_reversible_command ();
4240 MarkerDrag::aborted (bool movement_occurred)
4242 if (!movement_occurred) {
4246 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4248 /* move all markers to their original location */
4251 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4254 Location * location = _editor->find_location_from_marker (*m, is_start);
4257 (*m)->set_position (is_start ? location->start() : location->end());
4264 MarkerDrag::update_item (Location*)
4269 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4271 , _fixed_grab_x (0.0)
4272 , _fixed_grab_y (0.0)
4273 , _cumulative_x_drag (0.0)
4274 , _cumulative_y_drag (0.0)
4278 if (_zero_gain_fraction < 0.0) {
4279 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4282 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4284 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4290 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4292 Drag::start_grab (event, _editor->cursors()->fader);
4294 // start the grab at the center of the control point so
4295 // the point doesn't 'jump' to the mouse after the first drag
4296 _fixed_grab_x = _point->get_x();
4297 _fixed_grab_y = _point->get_y();
4299 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4300 setup_snap_delta (pos);
4302 float const fraction = 1 - (_point->get_y() / _point->line().height());
4303 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4305 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4307 if (!_point->can_slide ()) {
4308 _x_constrained = true;
4313 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4315 double dx = _drags->current_pointer_x() - last_pointer_x();
4316 double dy = current_pointer_y() - last_pointer_y();
4317 bool need_snap = true;
4319 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4325 /* coordinate in pixels relative to the start of the region (for region-based automation)
4326 or track (for track-based automation) */
4327 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4328 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4330 // calculate zero crossing point. back off by .01 to stay on the
4331 // positive side of zero
4332 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4334 if (_x_constrained) {
4337 if (_y_constrained) {
4341 _cumulative_x_drag = cx - _fixed_grab_x;
4342 _cumulative_y_drag = cy - _fixed_grab_y;
4346 cy = min ((double) _point->line().height(), cy);
4348 // make sure we hit zero when passing through
4349 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4353 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4355 if (!_x_constrained && need_snap) {
4356 _editor->snap_to_with_modifier (cx_frames, event);
4359 cx_frames -= snap_delta (event->button.state);
4360 cx_frames = min (cx_frames, _point->line().maximum_time());
4362 float const fraction = 1.0 - (cy / _point->line().height());
4365 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4366 _editor->begin_reversible_command (_("automation event move"));
4367 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4369 pair<double, float> result;
4370 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4372 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4376 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4378 if (!movement_occurred) {
4381 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4382 _editor->reset_point_selection ();
4386 _point->line().end_drag (_pushing, _final_index);
4387 _editor->commit_reversible_command ();
4392 ControlPointDrag::aborted (bool)
4394 _point->line().reset ();
4398 ControlPointDrag::active (Editing::MouseMode m)
4400 if (m == Editing::MouseDraw) {
4401 /* always active in mouse draw */
4405 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4406 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4409 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4412 , _fixed_grab_x (0.0)
4413 , _fixed_grab_y (0.0)
4414 , _cumulative_y_drag (0)
4418 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4422 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4424 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4427 _item = &_line->grab_item ();
4429 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4430 origin, and ditto for y.
4433 double mx = event->button.x;
4434 double my = event->button.y;
4436 _line->grab_item().canvas_to_item (mx, my);
4438 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4440 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4441 /* no adjacent points */
4445 Drag::start_grab (event, _editor->cursors()->fader);
4447 /* store grab start in item frame */
4448 double const bx = _line->nth (_before)->get_x();
4449 double const ax = _line->nth (_after)->get_x();
4450 double const click_ratio = (ax - mx) / (ax - bx);
4452 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4457 double fraction = 1.0 - (cy / _line->height());
4459 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4463 LineDrag::motion (GdkEvent* event, bool first_move)
4465 double dy = current_pointer_y() - last_pointer_y();
4467 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4471 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4473 _cumulative_y_drag = cy - _fixed_grab_y;
4476 cy = min ((double) _line->height(), cy);
4478 double const fraction = 1.0 - (cy / _line->height());
4482 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4484 _editor->begin_reversible_command (_("automation range move"));
4485 _line->start_drag_line (_before, _after, initial_fraction);
4488 /* we are ignoring x position for this drag, so we can just pass in anything */
4489 pair<double, float> result;
4491 result = _line->drag_motion (0, fraction, true, false, ignored);
4492 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4496 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4498 if (movement_occurred) {
4499 motion (event, false);
4500 _line->end_drag (false, 0);
4501 _editor->commit_reversible_command ();
4503 /* add a new control point on the line */
4505 AutomationTimeAxisView* atv;
4507 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4508 framepos_t where = grab_frame ();
4511 double cy = _fixed_grab_y;
4513 _line->grab_item().item_to_canvas (cx, cy);
4515 atv->add_automation_event (event, where, cy, false);
4516 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4517 AudioRegionView* arv;
4519 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4520 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4527 LineDrag::aborted (bool)
4532 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4536 _region_view_grab_x (0.0),
4537 _cumulative_x_drag (0),
4541 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4545 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4547 Drag::start_grab (event);
4549 _line = reinterpret_cast<Line*> (_item);
4552 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4554 double cx = event->button.x;
4555 double cy = event->button.y;
4557 _item->parent()->canvas_to_item (cx, cy);
4559 /* store grab start in parent frame */
4560 _region_view_grab_x = cx;
4562 _before = *(float*) _item->get_data ("position");
4564 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4566 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4570 FeatureLineDrag::motion (GdkEvent*, bool)
4572 double dx = _drags->current_pointer_x() - last_pointer_x();
4574 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4576 _cumulative_x_drag += dx;
4578 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4587 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4589 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4591 float *pos = new float;
4594 _line->set_data ("position", pos);
4600 FeatureLineDrag::finished (GdkEvent*, bool)
4602 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4603 _arv->update_transient(_before, _before);
4607 FeatureLineDrag::aborted (bool)
4612 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4614 , _vertical_only (false)
4616 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4620 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4622 Drag::start_grab (event);
4623 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4627 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4634 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4636 framepos_t grab = grab_frame ();
4637 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4638 _editor->snap_to_with_modifier (grab, event);
4640 grab = raw_grab_frame ();
4643 /* base start and end on initial click position */
4653 if (current_pointer_y() < grab_y()) {
4654 y1 = current_pointer_y();
4657 y2 = current_pointer_y();
4661 if (start != end || y1 != y2) {
4663 double x1 = _editor->sample_to_pixel (start);
4664 double x2 = _editor->sample_to_pixel (end);
4665 const double min_dimension = 2.0;
4667 if (_vertical_only) {
4668 /* fixed 10 pixel width */
4672 x2 = min (x1 - min_dimension, x2);
4674 x2 = max (x1 + min_dimension, x2);
4679 y2 = min (y1 - min_dimension, y2);
4681 y2 = max (y1 + min_dimension, y2);
4684 /* translate rect into item space and set */
4686 ArdourCanvas::Rect r (x1, y1, x2, y2);
4688 /* this drag is a _trackview_only == true drag, so the y1 and
4689 * y2 (computed using current_pointer_y() and grab_y()) will be
4690 * relative to the top of the trackview group). The
4691 * rubberband rect has the same parent/scroll offset as the
4692 * the trackview group, so we can use the "r" rect directly
4693 * to set the shape of the rubberband.
4696 _editor->rubberband_rect->set (r);
4697 _editor->rubberband_rect->show();
4698 _editor->rubberband_rect->raise_to_top();
4700 show_verbose_cursor_time (pf);
4702 do_select_things (event, true);
4707 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4711 framepos_t grab = grab_frame ();
4712 framepos_t lpf = last_pointer_frame ();
4714 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4715 grab = raw_grab_frame ();
4716 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4730 if (current_pointer_y() < grab_y()) {
4731 y1 = current_pointer_y();
4734 y2 = current_pointer_y();
4738 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4742 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4744 if (movement_occurred) {
4746 motion (event, false);
4747 do_select_things (event, false);
4753 bool do_deselect = true;
4754 MidiTimeAxisView* mtv;
4756 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4758 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4759 /* nothing selected */
4760 add_midi_region (mtv, true);
4761 do_deselect = false;
4765 /* do not deselect if Primary or Tertiary (toggle-select or
4766 * extend-select are pressed.
4769 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4770 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4777 _editor->rubberband_rect->hide();
4781 RubberbandSelectDrag::aborted (bool)
4783 _editor->rubberband_rect->hide ();
4786 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4787 : RegionDrag (e, i, p, v)
4789 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4793 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4795 Drag::start_grab (event, cursor);
4797 _editor->get_selection().add (_primary);
4799 framepos_t where = _primary->region()->position();
4800 setup_snap_delta (where);
4802 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4806 TimeFXDrag::motion (GdkEvent* event, bool)
4808 RegionView* rv = _primary;
4809 StreamView* cv = rv->get_time_axis_view().view ();
4811 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4812 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4813 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4814 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4815 _editor->snap_to_with_modifier (pf, event);
4816 pf -= snap_delta (event->button.state);
4818 if (pf > rv->region()->position()) {
4819 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4822 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4826 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4828 /* this may have been a single click, no drag. We still want the dialog
4829 to show up in that case, so that the user can manually edit the
4830 parameters for the timestretch.
4833 float fraction = 1.0;
4835 if (movement_occurred) {
4837 motion (event, false);
4839 _primary->get_time_axis_view().hide_timestretch ();
4841 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4843 if (adjusted_frame_pos < _primary->region()->position()) {
4844 /* backwards drag of the left edge - not usable */
4848 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4850 fraction = (double) newlen / (double) _primary->region()->length();
4852 #ifndef USE_RUBBERBAND
4853 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4854 if (_primary->region()->data_type() == DataType::AUDIO) {
4855 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4860 if (!_editor->get_selection().regions.empty()) {
4861 /* primary will already be included in the selection, and edit
4862 group shared editing will propagate selection across
4863 equivalent regions, so just use the current region
4867 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4868 error << _("An error occurred while executing time stretch operation") << endmsg;
4874 TimeFXDrag::aborted (bool)
4876 _primary->get_time_axis_view().hide_timestretch ();
4879 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4882 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4886 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4888 Drag::start_grab (event);
4892 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4894 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4898 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4900 if (movement_occurred && _editor->session()) {
4901 /* make sure we stop */
4902 _editor->session()->request_transport_speed (0.0);
4907 ScrubDrag::aborted (bool)
4912 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4916 , _time_selection_at_start (!_editor->get_selection().time.empty())
4918 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4920 if (_time_selection_at_start) {
4921 start_at_start = _editor->get_selection().time.start();
4922 end_at_start = _editor->get_selection().time.end_frame();
4927 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4929 if (_editor->session() == 0) {
4933 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4935 switch (_operation) {
4936 case CreateSelection:
4937 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4942 cursor = _editor->cursors()->selector;
4943 Drag::start_grab (event, cursor);
4946 case SelectionStartTrim:
4947 if (_editor->clicked_axisview) {
4948 _editor->clicked_axisview->order_selection_trims (_item, true);
4950 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4953 case SelectionEndTrim:
4954 if (_editor->clicked_axisview) {
4955 _editor->clicked_axisview->order_selection_trims (_item, false);
4957 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4961 Drag::start_grab (event, cursor);
4964 case SelectionExtend:
4965 Drag::start_grab (event, cursor);
4969 if (_operation == SelectionMove) {
4970 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4972 show_verbose_cursor_time (adjusted_current_frame (event));
4977 SelectionDrag::setup_pointer_frame_offset ()
4979 switch (_operation) {
4980 case CreateSelection:
4981 _pointer_frame_offset = 0;
4984 case SelectionStartTrim:
4986 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4989 case SelectionEndTrim:
4990 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4993 case SelectionExtend:
4999 SelectionDrag::motion (GdkEvent* event, bool first_move)
5001 framepos_t start = 0;
5003 framecnt_t length = 0;
5004 framecnt_t distance = 0;
5006 framepos_t const pending_position = adjusted_current_frame (event);
5008 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5012 switch (_operation) {
5013 case CreateSelection:
5015 framepos_t grab = grab_frame ();
5018 grab = adjusted_current_frame (event, false);
5019 if (grab < pending_position) {
5020 _editor->snap_to (grab, RoundDownMaybe);
5022 _editor->snap_to (grab, RoundUpMaybe);
5026 if (pending_position < grab) {
5027 start = pending_position;
5030 end = pending_position;
5034 /* first drag: Either add to the selection
5035 or create a new selection
5042 /* adding to the selection */
5043 _editor->set_selected_track_as_side_effect (Selection::Add);
5044 _editor->clicked_selection = _editor->selection->add (start, end);
5051 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5052 _editor->set_selected_track_as_side_effect (Selection::Set);
5055 _editor->clicked_selection = _editor->selection->set (start, end);
5059 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5060 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5061 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5063 _editor->selection->add (atest);
5067 /* select all tracks within the rectangle that we've marked out so far */
5068 TrackViewList new_selection;
5069 TrackViewList& all_tracks (_editor->track_views);
5071 ArdourCanvas::Coord const top = grab_y();
5072 ArdourCanvas::Coord const bottom = current_pointer_y();
5074 if (top >= 0 && bottom >= 0) {
5076 //first, find the tracks that are covered in the y range selection
5077 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5078 if ((*i)->covered_by_y_range (top, bottom)) {
5079 new_selection.push_back (*i);
5083 //now find any tracks that are GROUPED with the tracks we selected
5084 TrackViewList grouped_add = new_selection;
5085 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5086 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5087 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5088 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5089 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5090 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5091 grouped_add.push_back (*j);
5096 //now compare our list with the current selection, and add or remove as necessary
5097 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5098 TrackViewList tracks_to_add;
5099 TrackViewList tracks_to_remove;
5100 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5101 if ( !_editor->selection->tracks.contains ( *i ) )
5102 tracks_to_add.push_back ( *i );
5103 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5104 if ( !grouped_add.contains ( *i ) )
5105 tracks_to_remove.push_back ( *i );
5106 _editor->selection->add(tracks_to_add);
5107 _editor->selection->remove(tracks_to_remove);
5113 case SelectionStartTrim:
5115 end = _editor->selection->time[_editor->clicked_selection].end;
5117 if (pending_position > end) {
5120 start = pending_position;
5124 case SelectionEndTrim:
5126 start = _editor->selection->time[_editor->clicked_selection].start;
5128 if (pending_position < start) {
5131 end = pending_position;
5138 start = _editor->selection->time[_editor->clicked_selection].start;
5139 end = _editor->selection->time[_editor->clicked_selection].end;
5141 length = end - start;
5142 distance = pending_position - start;
5143 start = pending_position;
5144 _editor->snap_to (start);
5146 end = start + length;
5150 case SelectionExtend:
5155 switch (_operation) {
5157 if (_time_selection_at_start) {
5158 _editor->selection->move_time (distance);
5162 _editor->selection->replace (_editor->clicked_selection, start, end);
5166 if (_operation == SelectionMove) {
5167 show_verbose_cursor_time(start);
5169 show_verbose_cursor_time(pending_position);
5174 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5176 Session* s = _editor->session();
5178 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5179 if (movement_occurred) {
5180 motion (event, false);
5181 /* XXX this is not object-oriented programming at all. ick */
5182 if (_editor->selection->time.consolidate()) {
5183 _editor->selection->TimeChanged ();
5186 /* XXX what if its a music time selection? */
5188 if (s->get_play_range() && s->transport_rolling()) {
5189 s->request_play_range (&_editor->selection->time, true);
5190 } else if (!s->config.get_external_sync()) {
5191 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5192 if (_operation == SelectionEndTrim)
5193 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5195 s->request_locate (_editor->get_selection().time.start());
5199 if (_editor->get_selection().time.length() != 0) {
5200 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5202 s->clear_range_selection ();
5207 /* just a click, no pointer movement.
5210 if (_operation == SelectionExtend) {
5211 if (_time_selection_at_start) {
5212 framepos_t pos = adjusted_current_frame (event, false);
5213 framepos_t start = min (pos, start_at_start);
5214 framepos_t end = max (pos, end_at_start);
5215 _editor->selection->set (start, end);
5218 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5219 if (_editor->clicked_selection) {
5220 _editor->selection->remove (_editor->clicked_selection);
5223 if (!_editor->clicked_selection) {
5224 _editor->selection->clear_time();
5229 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5230 _editor->selection->set (_editor->clicked_axisview);
5233 if (s && s->get_play_range () && s->transport_rolling()) {
5234 s->request_stop (false, false);
5239 _editor->stop_canvas_autoscroll ();
5240 _editor->clicked_selection = 0;
5241 _editor->commit_reversible_selection_op ();
5245 SelectionDrag::aborted (bool)
5250 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5251 : Drag (e, i, false),
5255 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5257 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5258 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5259 physical_screen_height (_editor->current_toplevel()->get_window())));
5260 _drag_rect->hide ();
5262 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5263 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5266 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5268 /* normal canvas items will be cleaned up when their parent group is deleted. But
5269 this item is created as the child of a long-lived parent group, and so we
5270 need to explicitly delete it.
5276 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5278 if (_editor->session() == 0) {
5282 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5284 if (!_editor->temp_location) {
5285 _editor->temp_location = new Location (*_editor->session());
5288 switch (_operation) {
5289 case CreateSkipMarker:
5290 case CreateRangeMarker:
5291 case CreateTransportMarker:
5292 case CreateCDMarker:
5294 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5299 cursor = _editor->cursors()->selector;
5303 Drag::start_grab (event, cursor);
5305 show_verbose_cursor_time (adjusted_current_frame (event));
5309 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5311 framepos_t start = 0;
5313 ArdourCanvas::Rectangle *crect;
5315 switch (_operation) {
5316 case CreateSkipMarker:
5317 crect = _editor->range_bar_drag_rect;
5319 case CreateRangeMarker:
5320 crect = _editor->range_bar_drag_rect;
5322 case CreateTransportMarker:
5323 crect = _editor->transport_bar_drag_rect;
5325 case CreateCDMarker:
5326 crect = _editor->cd_marker_bar_drag_rect;
5329 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5334 framepos_t const pf = adjusted_current_frame (event);
5336 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5337 framepos_t grab = grab_frame ();
5338 _editor->snap_to (grab);
5340 if (pf < grab_frame()) {
5348 /* first drag: Either add to the selection
5349 or create a new selection.
5354 _editor->temp_location->set (start, end);
5358 update_item (_editor->temp_location);
5360 //_drag_rect->raise_to_top();
5366 _editor->temp_location->set (start, end);
5368 double x1 = _editor->sample_to_pixel (start);
5369 double x2 = _editor->sample_to_pixel (end);
5373 update_item (_editor->temp_location);
5376 show_verbose_cursor_time (pf);
5381 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5383 Location * newloc = 0;
5387 if (movement_occurred) {
5388 motion (event, false);
5391 switch (_operation) {
5392 case CreateSkipMarker:
5393 case CreateRangeMarker:
5394 case CreateCDMarker:
5396 XMLNode &before = _editor->session()->locations()->get_state();
5397 if (_operation == CreateSkipMarker) {
5398 _editor->begin_reversible_command (_("new skip marker"));
5399 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5400 flags = Location::IsRangeMarker | Location::IsSkip;
5401 _editor->range_bar_drag_rect->hide();
5402 } else if (_operation == CreateCDMarker) {
5403 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5404 _editor->begin_reversible_command (_("new CD marker"));
5405 flags = Location::IsRangeMarker | Location::IsCDMarker;
5406 _editor->cd_marker_bar_drag_rect->hide();
5408 _editor->begin_reversible_command (_("new skip marker"));
5409 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5410 flags = Location::IsRangeMarker;
5411 _editor->range_bar_drag_rect->hide();
5413 newloc = new Location (
5414 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5417 _editor->session()->locations()->add (newloc, true);
5418 XMLNode &after = _editor->session()->locations()->get_state();
5419 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5420 _editor->commit_reversible_command ();
5424 case CreateTransportMarker:
5425 // popup menu to pick loop or punch
5426 _editor->new_transport_marker_context_menu (&event->button, _item);
5432 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5434 if (_operation == CreateTransportMarker) {
5436 /* didn't drag, so just locate */
5438 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5440 } else if (_operation == CreateCDMarker) {
5442 /* didn't drag, but mark is already created so do
5445 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5450 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5452 if (end == max_framepos) {
5453 end = _editor->session()->current_end_frame ();
5456 if (start == max_framepos) {
5457 start = _editor->session()->current_start_frame ();
5460 switch (_editor->mouse_mode) {
5462 /* find the two markers on either side and then make the selection from it */
5463 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5467 /* find the two markers on either side of the click and make the range out of it */
5468 _editor->selection->set (start, end);
5477 _editor->stop_canvas_autoscroll ();
5481 RangeMarkerBarDrag::aborted (bool movement_occurred)
5483 if (movement_occurred) {
5484 _drag_rect->hide ();
5489 RangeMarkerBarDrag::update_item (Location* location)
5491 double const x1 = _editor->sample_to_pixel (location->start());
5492 double const x2 = _editor->sample_to_pixel (location->end());
5494 _drag_rect->set_x0 (x1);
5495 _drag_rect->set_x1 (x2);
5498 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5500 , _cumulative_dx (0)
5501 , _cumulative_dy (0)
5502 , _was_selected (false)
5504 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5506 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5508 _region = &_primary->region_view ();
5509 _note_height = _region->midi_stream_view()->note_height ();
5513 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5515 Drag::start_grab (event);
5516 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5518 if (!(_was_selected = _primary->selected())) {
5520 /* tertiary-click means extend selection - we'll do that on button release,
5521 so don't add it here, because otherwise we make it hard to figure
5522 out the "extend-to" range.
5525 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5528 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5531 _region->note_selected (_primary, true);
5533 _editor->get_selection().clear_points();
5534 _region->unique_select (_primary);
5540 /** @return Current total drag x change in frames */
5542 NoteDrag::total_dx (const guint state) const
5545 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5547 /* primary note time */
5548 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5550 /* new time of the primary note in session frames */
5551 frameoffset_t st = n + dx + snap_delta (state);
5553 framepos_t const rp = _region->region()->position ();
5555 /* prevent the note being dragged earlier than the region's position */
5558 /* possibly snap and return corresponding delta */
5562 if (ArdourKeyboard::indicates_snap (state)) {
5563 if (_editor->snap_mode () != SnapOff) {
5567 if (_editor->snap_mode () == SnapOff) {
5569 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5570 if (ArdourKeyboard::indicates_snap_delta (state)) {
5578 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5579 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5581 ret = st - n - snap_delta (state);
5586 /** @return Current total drag y change in note number */
5588 NoteDrag::total_dy () const
5590 MidiStreamView* msv = _region->midi_stream_view ();
5591 double const y = _region->midi_view()->y_position ();
5592 /* new current note */
5593 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5595 n = max (msv->lowest_note(), n);
5596 n = min (msv->highest_note(), n);
5597 /* and work out delta */
5598 return n - msv->y_to_note (grab_y() - y);
5602 NoteDrag::motion (GdkEvent * event, bool)
5604 /* Total change in x and y since the start of the drag */
5605 frameoffset_t const dx = total_dx (event->button.state);
5606 int8_t const dy = total_dy ();
5608 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5609 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5610 double const tdy = -dy * _note_height - _cumulative_dy;
5613 _cumulative_dx += tdx;
5614 _cumulative_dy += tdy;
5616 int8_t note_delta = total_dy();
5618 _region->move_selection (tdx, tdy, note_delta);
5620 /* the new note value may be the same as the old one, but we
5621 * don't know what that means because the selection may have
5622 * involved more than one note and we might be doing something
5623 * odd with them. so show the note value anyway, always.
5626 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5628 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5633 NoteDrag::finished (GdkEvent* ev, bool moved)
5636 /* no motion - select note */
5638 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5639 _editor->current_mouse_mode() == Editing::MouseDraw) {
5641 bool changed = false;
5643 if (_was_selected) {
5644 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5646 _region->note_deselected (_primary);
5649 _editor->get_selection().clear_points();
5650 _region->unique_select (_primary);
5654 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5655 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5657 if (!extend && !add && _region->selection_size() > 1) {
5658 _editor->get_selection().clear_points();
5659 _region->unique_select (_primary);
5661 } else if (extend) {
5662 _region->note_selected (_primary, true, true);
5665 /* it was added during button press */
5672 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5673 _editor->commit_reversible_selection_op();
5677 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5682 NoteDrag::aborted (bool)
5687 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5688 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5689 : Drag (editor, atv->base_item ())
5691 , _y_origin (atv->y_position())
5692 , _nothing_to_drag (false)
5694 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5695 setup (atv->lines ());
5698 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5699 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5700 : Drag (editor, rv->get_canvas_group ())
5702 , _y_origin (rv->get_time_axis_view().y_position())
5703 , _nothing_to_drag (false)
5706 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5708 list<boost::shared_ptr<AutomationLine> > lines;
5710 AudioRegionView* audio_view;
5711 AutomationRegionView* automation_view;
5712 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5713 lines.push_back (audio_view->get_gain_line ());
5714 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5715 lines.push_back (automation_view->line ());
5718 error << _("Automation range drag created for invalid region type") << endmsg;
5724 /** @param lines AutomationLines to drag.
5725 * @param offset Offset from the session start to the points in the AutomationLines.
5728 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5730 /* find the lines that overlap the ranges being dragged */
5731 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5732 while (i != lines.end ()) {
5733 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5736 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5738 /* check this range against all the AudioRanges that we are using */
5739 list<AudioRange>::const_iterator k = _ranges.begin ();
5740 while (k != _ranges.end()) {
5741 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5747 /* add it to our list if it overlaps at all */
5748 if (k != _ranges.end()) {
5753 _lines.push_back (n);
5759 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5763 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5765 return 1.0 - ((global_y - _y_origin) / line->height());
5769 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5771 const double v = list->eval(x);
5772 return _integral ? rint(v) : v;
5776 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5778 Drag::start_grab (event, cursor);
5780 /* Get line states before we start changing things */
5781 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5782 i->state = &i->line->get_state ();
5783 i->original_fraction = y_fraction (i->line, current_pointer_y());
5786 if (_ranges.empty()) {
5788 /* No selected time ranges: drag all points */
5789 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5790 uint32_t const N = i->line->npoints ();
5791 for (uint32_t j = 0; j < N; ++j) {
5792 i->points.push_back (i->line->nth (j));
5798 if (_nothing_to_drag) {
5804 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5806 if (_nothing_to_drag && !first_move) {
5811 _editor->begin_reversible_command (_("automation range move"));
5813 if (!_ranges.empty()) {
5815 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5817 framecnt_t const half = (i->start + i->end) / 2;
5819 /* find the line that this audio range starts in */
5820 list<Line>::iterator j = _lines.begin();
5821 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5825 if (j != _lines.end()) {
5826 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5828 /* j is the line that this audio range starts in; fade into it;
5829 64 samples length plucked out of thin air.
5832 framepos_t a = i->start + 64;
5837 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5838 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5840 XMLNode &before = the_list->get_state();
5841 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5842 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5844 if (add_p || add_q) {
5845 _editor->session()->add_command (
5846 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5850 /* same thing for the end */
5853 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5857 if (j != _lines.end()) {
5858 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5860 /* j is the line that this audio range starts in; fade out of it;
5861 64 samples length plucked out of thin air.
5864 framepos_t b = i->end - 64;
5869 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5870 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5872 XMLNode &before = the_list->get_state();
5873 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5874 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5876 if (add_p || add_q) {
5877 _editor->session()->add_command (
5878 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5883 _nothing_to_drag = true;
5885 /* Find all the points that should be dragged and put them in the relevant
5886 points lists in the Line structs.
5889 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5891 uint32_t const N = i->line->npoints ();
5892 for (uint32_t j = 0; j < N; ++j) {
5894 /* here's a control point on this line */
5895 ControlPoint* p = i->line->nth (j);
5896 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5898 /* see if it's inside a range */
5899 list<AudioRange>::const_iterator k = _ranges.begin ();
5900 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5904 if (k != _ranges.end()) {
5905 /* dragging this point */
5906 _nothing_to_drag = false;
5907 i->points.push_back (p);
5913 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5914 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5918 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5919 float const f = y_fraction (l->line, current_pointer_y());
5920 /* we are ignoring x position for this drag, so we can just pass in anything */
5921 pair<double, float> result;
5923 result = l->line->drag_motion (0, f, true, false, ignored);
5924 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5929 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5931 if (_nothing_to_drag || !motion_occurred) {
5935 motion (event, false);
5936 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5937 i->line->end_drag (false, 0);
5940 _editor->commit_reversible_command ();
5944 AutomationRangeDrag::aborted (bool)
5946 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5951 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5953 , initial_time_axis_view (itav)
5955 /* note that time_axis_view may be null if the regionview was created
5956 * as part of a copy operation.
5958 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5959 layer = v->region()->layer ();
5960 initial_y = v->get_canvas_group()->position().y;
5961 initial_playlist = v->region()->playlist ();
5962 initial_position = v->region()->position ();
5963 initial_end = v->region()->position () + v->region()->length ();
5966 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5967 : Drag (e, i->canvas_item ())
5970 , _cumulative_dx (0)
5972 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5973 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5978 PatchChangeDrag::motion (GdkEvent* ev, bool)
5980 framepos_t f = adjusted_current_frame (ev);
5981 boost::shared_ptr<Region> r = _region_view->region ();
5982 f = max (f, r->position ());
5983 f = min (f, r->last_frame ());
5985 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5986 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5987 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5988 _cumulative_dx = dxu;
5992 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5994 if (!movement_occurred) {
5995 if (was_double_click()) {
5996 _region_view->edit_patch_change (_patch_change);
6001 boost::shared_ptr<Region> r (_region_view->region ());
6002 framepos_t f = adjusted_current_frame (ev);
6003 f = max (f, r->position ());
6004 f = min (f, r->last_frame ());
6006 _region_view->move_patch_change (
6008 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6013 PatchChangeDrag::aborted (bool)
6015 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6019 PatchChangeDrag::setup_pointer_frame_offset ()
6021 boost::shared_ptr<Region> region = _region_view->region ();
6022 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6025 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6026 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6033 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6035 _region_view->update_drag_selection (
6037 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6041 MidiRubberbandSelectDrag::deselect_things ()
6046 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6047 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6050 _vertical_only = true;
6054 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6056 double const y = _region_view->midi_view()->y_position ();
6058 y1 = max (0.0, y1 - y);
6059 y2 = max (0.0, y2 - y);
6061 _region_view->update_vertical_drag_selection (
6064 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6069 MidiVerticalSelectDrag::deselect_things ()
6074 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6075 : RubberbandSelectDrag (e, i)
6081 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6083 if (drag_in_progress) {
6084 /* We just want to select things at the end of the drag, not during it */
6088 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6090 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6092 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6094 _editor->commit_reversible_selection_op ();
6098 EditorRubberbandSelectDrag::deselect_things ()
6100 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6102 _editor->selection->clear_tracks();
6103 _editor->selection->clear_regions();
6104 _editor->selection->clear_points ();
6105 _editor->selection->clear_lines ();
6106 _editor->selection->clear_midi_notes ();
6108 _editor->commit_reversible_selection_op();
6111 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6116 _note[0] = _note[1] = 0;
6119 NoteCreateDrag::~NoteCreateDrag ()
6125 NoteCreateDrag::grid_frames (framepos_t t) const
6128 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6130 grid_beats = Evoral::Beats(1);
6133 return _region_view->region_beats_to_region_frames (grid_beats);
6137 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6139 Drag::start_grab (event, cursor);
6141 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6143 framepos_t pf = _drags->current_pointer_frame ();
6144 framecnt_t const g = grid_frames (pf);
6146 /* Hack so that we always snap to the note that we are over, instead of snapping
6147 to the next one if we're more than halfway through the one we're over.
6149 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6153 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6154 _note[1] = _note[0];
6156 MidiStreamView* sv = _region_view->midi_stream_view ();
6157 double const x = _editor->sample_to_pixel (_note[0]);
6158 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6160 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6161 _drag_rect->set_outline_all ();
6162 _drag_rect->set_outline_color (0xffffff99);
6163 _drag_rect->set_fill_color (0xffffff66);
6167 NoteCreateDrag::motion (GdkEvent* event, bool)
6169 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6170 double const x0 = _editor->sample_to_pixel (_note[0]);
6171 double const x1 = _editor->sample_to_pixel (_note[1]);
6172 _drag_rect->set_x0 (std::min(x0, x1));
6173 _drag_rect->set_x1 (std::max(x0, x1));
6177 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6179 if (!had_movement) {
6183 framepos_t const start = min (_note[0], _note[1]);
6184 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6186 framecnt_t const g = grid_frames (start);
6187 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6189 if (_editor->snap_mode() == SnapNormal && length < g) {
6193 Evoral::Beats length_beats = max (
6194 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6196 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6200 NoteCreateDrag::y_to_region (double y) const
6203 _region_view->get_canvas_group()->canvas_to_item (x, y);
6208 NoteCreateDrag::aborted (bool)
6213 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6218 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6222 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6224 Drag::start_grab (event, cursor);
6228 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6234 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6237 distance = _drags->current_pointer_x() - grab_x();
6238 len = ar->fade_in()->back()->when;
6240 distance = grab_x() - _drags->current_pointer_x();
6241 len = ar->fade_out()->back()->when;
6244 /* how long should it be ? */
6246 new_length = len + _editor->pixel_to_sample (distance);
6248 /* now check with the region that this is legal */
6250 new_length = ar->verify_xfade_bounds (new_length, start);
6253 arv->reset_fade_in_shape_width (ar, new_length);
6255 arv->reset_fade_out_shape_width (ar, new_length);
6260 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6266 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6269 distance = _drags->current_pointer_x() - grab_x();
6270 len = ar->fade_in()->back()->when;
6272 distance = grab_x() - _drags->current_pointer_x();
6273 len = ar->fade_out()->back()->when;
6276 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6278 _editor->begin_reversible_command ("xfade trim");
6279 ar->playlist()->clear_owned_changes ();
6282 ar->set_fade_in_length (new_length);
6284 ar->set_fade_out_length (new_length);
6287 /* Adjusting the xfade may affect other regions in the playlist, so we need
6288 to get undo Commands from the whole playlist rather than just the
6292 vector<Command*> cmds;
6293 ar->playlist()->rdiff (cmds);
6294 _editor->session()->add_commands (cmds);
6295 _editor->commit_reversible_command ();
6300 CrossfadeEdgeDrag::aborted (bool)
6303 // arv->redraw_start_xfade ();
6305 // arv->redraw_end_xfade ();
6309 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6310 : Drag (e, item, true)
6311 , line (new EditorCursor (*e))
6313 line->set_position (pos);
6317 RegionCutDrag::~RegionCutDrag ()
6323 RegionCutDrag::motion (GdkEvent*, bool)
6325 framepos_t where = _drags->current_pointer_frame();
6326 _editor->snap_to (where);
6328 line->set_position (where);
6332 RegionCutDrag::finished (GdkEvent*, bool)
6334 _editor->get_track_canvas()->canvas()->re_enter();
6336 framepos_t pos = _drags->current_pointer_frame();
6340 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6346 _editor->split_regions_at (pos, rs);
6350 RegionCutDrag::aborted (bool)