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() < b->presentation_info();
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() - _marker->tempo().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 TempoMap& map (_editor->session()->tempo_map());
3339 const bool was_music = _real_section->position_lock_style() == MusicTime;
3341 const framepos_t pf = adjusted_current_frame (event);
3343 if (!_editor->snap_musical()) {
3346 _real_section->set_position_lock_style (AudioTime);
3349 map.gui_move_tempo (_real_section, pf);
3352 _real_section->set_position_lock_style (MusicTime);
3358 _real_section->set_position_lock_style (MusicTime);
3361 map.gui_move_tempo (_real_section, pf);
3364 _real_section->set_position_lock_style (AudioTime);
3368 show_verbose_cursor_time (_real_section->frame());
3371 /* this has moved the bar lines themselves, so recalibrate the offset */
3372 setup_pointer_frame_offset();
3374 _marker->set_position (adjusted_current_frame (event, false));
3378 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3380 if (!_real_section->active()) {
3383 if (!movement_occurred) {
3384 if (was_double_click()) {
3385 _editor->edit_tempo_marker (*_marker);
3390 TempoMap& map (_editor->session()->tempo_map());
3392 XMLNode &after = map.get_state();
3393 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3394 _editor->commit_reversible_command ();
3396 // delete the dummy marker we used for visual representation while moving.
3397 // a new visual marker will show up automatically.
3402 TempoMarkerDrag::aborted (bool moved)
3404 _marker->set_position (_marker->tempo().frame());
3406 TempoMap& map (_editor->session()->tempo_map());
3407 map.set_state (*before_state, Stateful::current_state_version);
3408 // delete the dummy marker we used for visual representation while moving.
3409 // a new visual marker will show up automatically.
3414 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3420 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3425 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3427 Drag::start_grab (event, cursor);
3428 TempoMap& map (_editor->session()->tempo_map());
3429 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3432 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3433 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3434 show_verbose_cursor_text (sstr.str());
3435 finished (event, false);
3439 BBTRulerDrag::setup_pointer_frame_offset ()
3441 TempoMap& map (_editor->session()->tempo_map());
3442 const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3443 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3446 if (divisions > 0) {
3447 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3449 /* while it makes some sense for the user to determine the division to 'grab',
3450 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3451 and the result over steep tempo curves. Use sixteenths.
3453 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3456 _pulse = map.pulse_at_beat (beat);
3458 _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3463 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3465 TempoMap& map (_editor->session()->tempo_map());
3468 /* get current state */
3469 before_state = &map.get_state();
3470 _editor->begin_reversible_command (_("dilate tempo"));
3475 if (_editor->snap_musical()) {
3476 pf = adjusted_current_frame (event, false);
3478 pf = adjusted_current_frame (event);
3481 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3482 /* adjust previous tempo to match pointer frame */
3483 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3486 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3487 sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3488 show_verbose_cursor_text (sstr.str());
3492 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3494 if (!movement_occurred) {
3498 TempoMap& map (_editor->session()->tempo_map());
3500 XMLNode &after = map.get_state();
3501 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3502 _editor->commit_reversible_command ();
3506 BBTRulerDrag::aborted (bool moved)
3509 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3514 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3515 : Drag (e, &c.track_canvas_item(), false)
3520 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3523 /** Do all the things we do when dragging the playhead to make it look as though
3524 * we have located, without actually doing the locate (because that would cause
3525 * the diskstream buffers to be refilled, which is too slow).
3528 CursorDrag::fake_locate (framepos_t t)
3530 if (_editor->session () == 0) {
3534 _editor->playhead_cursor->set_position (t);
3536 Session* s = _editor->session ();
3537 if (s->timecode_transmission_suspended ()) {
3538 framepos_t const f = _editor->playhead_cursor->current_frame ();
3539 /* This is asynchronous so it will be sent "now"
3541 s->send_mmc_locate (f);
3542 /* These are synchronous and will be sent during the next
3545 s->queue_full_time_code ();
3546 s->queue_song_position_pointer ();
3549 show_verbose_cursor_time (t);
3550 _editor->UpdateAllTransportClocks (t);
3554 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3556 Drag::start_grab (event, c);
3557 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3559 _grab_zoom = _editor->samples_per_pixel;
3561 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3563 _editor->snap_to_with_modifier (where, event);
3565 _editor->_dragging_playhead = true;
3567 Session* s = _editor->session ();
3569 /* grab the track canvas item as well */
3571 _cursor.track_canvas_item().grab();
3574 if (_was_rolling && _stop) {
3578 if (s->is_auditioning()) {
3579 s->cancel_audition ();
3583 if (AudioEngine::instance()->connected()) {
3585 /* do this only if we're the engine is connected
3586 * because otherwise this request will never be
3587 * serviced and we'll busy wait forever. likewise,
3588 * notice if we are disconnected while waiting for the
3589 * request to be serviced.
3592 s->request_suspend_timecode_transmission ();
3593 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3594 /* twiddle our thumbs */
3599 fake_locate (where - snap_delta (event->button.state));
3603 CursorDrag::motion (GdkEvent* event, bool)
3605 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3606 _editor->snap_to_with_modifier (where, event);
3607 if (where != last_pointer_frame()) {
3608 fake_locate (where - snap_delta (event->button.state));
3613 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3615 _editor->_dragging_playhead = false;
3617 _cursor.track_canvas_item().ungrab();
3619 if (!movement_occurred && _stop) {
3623 motion (event, false);
3625 Session* s = _editor->session ();
3627 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3628 _editor->_pending_locate_request = true;
3629 s->request_resume_timecode_transmission ();
3634 CursorDrag::aborted (bool)
3636 _cursor.track_canvas_item().ungrab();
3638 if (_editor->_dragging_playhead) {
3639 _editor->session()->request_resume_timecode_transmission ();
3640 _editor->_dragging_playhead = false;
3643 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3646 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3647 : RegionDrag (e, i, p, v)
3649 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3653 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3655 Drag::start_grab (event, cursor);
3657 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3658 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3659 setup_snap_delta (r->position ());
3661 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3665 FadeInDrag::setup_pointer_frame_offset ()
3667 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3668 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3669 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3673 FadeInDrag::motion (GdkEvent* event, bool)
3675 framecnt_t fade_length;
3677 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3678 _editor->snap_to_with_modifier (pos, event);
3679 pos -= snap_delta (event->button.state);
3681 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3683 if (pos < (region->position() + 64)) {
3684 fade_length = 64; // this should be a minimum defined somewhere
3685 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3686 fade_length = region->length() - region->fade_out()->back()->when - 1;
3688 fade_length = pos - region->position();
3691 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3693 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3699 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3702 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3706 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3708 if (!movement_occurred) {
3712 framecnt_t fade_length;
3713 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3714 _editor->snap_to_with_modifier (pos, event);
3715 pos -= snap_delta (event->button.state);
3717 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3719 if (pos < (region->position() + 64)) {
3720 fade_length = 64; // this should be a minimum defined somewhere
3721 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3722 fade_length = region->length() - region->fade_out()->back()->when - 1;
3724 fade_length = pos - region->position();
3727 bool in_command = false;
3729 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3731 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3737 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3738 XMLNode &before = alist->get_state();
3740 tmp->audio_region()->set_fade_in_length (fade_length);
3741 tmp->audio_region()->set_fade_in_active (true);
3744 _editor->begin_reversible_command (_("change fade in length"));
3747 XMLNode &after = alist->get_state();
3748 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3752 _editor->commit_reversible_command ();
3757 FadeInDrag::aborted (bool)
3759 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3760 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3766 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3770 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3771 : RegionDrag (e, i, p, v)
3773 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3777 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3779 Drag::start_grab (event, cursor);
3781 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3782 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3783 setup_snap_delta (r->last_frame ());
3785 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3789 FadeOutDrag::setup_pointer_frame_offset ()
3791 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3792 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3793 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3797 FadeOutDrag::motion (GdkEvent* event, bool)
3799 framecnt_t fade_length;
3801 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3802 _editor->snap_to_with_modifier (pos, event);
3803 pos -= snap_delta (event->button.state);
3805 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3807 if (pos > (region->last_frame() - 64)) {
3808 fade_length = 64; // this should really be a minimum fade defined somewhere
3809 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3810 fade_length = region->length() - region->fade_in()->back()->when - 1;
3812 fade_length = region->last_frame() - pos;
3815 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3817 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3823 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3826 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3830 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3832 if (!movement_occurred) {
3836 framecnt_t fade_length;
3838 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3839 _editor->snap_to_with_modifier (pos, event);
3840 pos -= snap_delta (event->button.state);
3842 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3844 if (pos > (region->last_frame() - 64)) {
3845 fade_length = 64; // this should really be a minimum fade defined somewhere
3846 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3847 fade_length = region->length() - region->fade_in()->back()->when - 1;
3849 fade_length = region->last_frame() - pos;
3852 bool in_command = false;
3854 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3856 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3862 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3863 XMLNode &before = alist->get_state();
3865 tmp->audio_region()->set_fade_out_length (fade_length);
3866 tmp->audio_region()->set_fade_out_active (true);
3869 _editor->begin_reversible_command (_("change fade out length"));
3872 XMLNode &after = alist->get_state();
3873 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3877 _editor->commit_reversible_command ();
3882 FadeOutDrag::aborted (bool)
3884 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3885 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3891 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3895 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3897 , _selection_changed (false)
3899 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3900 Gtk::Window* toplevel = _editor->current_toplevel();
3901 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3905 _points.push_back (ArdourCanvas::Duple (0, 0));
3907 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3910 MarkerDrag::~MarkerDrag ()
3912 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3917 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3919 location = new Location (*l);
3920 markers.push_back (m);
3925 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3927 Drag::start_grab (event, cursor);
3931 Location *location = _editor->find_location_from_marker (_marker, is_start);
3932 _editor->_dragging_edit_point = true;
3934 update_item (location);
3936 // _drag_line->show();
3937 // _line->raise_to_top();
3940 show_verbose_cursor_time (location->start());
3942 show_verbose_cursor_time (location->end());
3944 setup_snap_delta (is_start ? location->start() : location->end());
3946 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3949 case Selection::Toggle:
3950 /* we toggle on the button release */
3952 case Selection::Set:
3953 if (!_editor->selection->selected (_marker)) {
3954 _editor->selection->set (_marker);
3955 _selection_changed = true;
3958 case Selection::Extend:
3960 Locations::LocationList ll;
3961 list<ArdourMarker*> to_add;
3963 _editor->selection->markers.range (s, e);
3964 s = min (_marker->position(), s);
3965 e = max (_marker->position(), e);
3968 if (e < max_framepos) {
3971 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3972 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3973 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3976 to_add.push_back (lm->start);
3979 to_add.push_back (lm->end);
3983 if (!to_add.empty()) {
3984 _editor->selection->add (to_add);
3985 _selection_changed = true;
3989 case Selection::Add:
3990 _editor->selection->add (_marker);
3991 _selection_changed = true;
3996 /* Set up copies for us to manipulate during the drag
3999 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4001 Location* l = _editor->find_location_from_marker (*i, is_start);
4008 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4010 /* range: check that the other end of the range isn't
4013 CopiedLocationInfo::iterator x;
4014 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4015 if (*(*x).location == *l) {
4019 if (x == _copied_locations.end()) {
4020 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4022 (*x).markers.push_back (*i);
4023 (*x).move_both = true;
4031 MarkerDrag::setup_pointer_frame_offset ()
4034 Location *location = _editor->find_location_from_marker (_marker, is_start);
4035 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4039 MarkerDrag::motion (GdkEvent* event, bool)
4041 framecnt_t f_delta = 0;
4043 bool move_both = false;
4044 Location *real_location;
4045 Location *copy_location = 0;
4046 framecnt_t const sd = snap_delta (event->button.state);
4048 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4049 framepos_t next = newframe;
4051 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4055 CopiedLocationInfo::iterator x;
4057 /* find the marker we're dragging, and compute the delta */
4059 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4061 copy_location = (*x).location;
4063 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4065 /* this marker is represented by this
4066 * CopiedLocationMarkerInfo
4069 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4074 if (real_location->is_mark()) {
4075 f_delta = newframe - copy_location->start();
4079 switch (_marker->type()) {
4080 case ArdourMarker::SessionStart:
4081 case ArdourMarker::RangeStart:
4082 case ArdourMarker::LoopStart:
4083 case ArdourMarker::PunchIn:
4084 f_delta = newframe - copy_location->start();
4087 case ArdourMarker::SessionEnd:
4088 case ArdourMarker::RangeEnd:
4089 case ArdourMarker::LoopEnd:
4090 case ArdourMarker::PunchOut:
4091 f_delta = newframe - copy_location->end();
4094 /* what kind of marker is this ? */
4103 if (x == _copied_locations.end()) {
4104 /* hmm, impossible - we didn't find the dragged marker */
4108 /* now move them all */
4110 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4112 copy_location = x->location;
4114 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4118 if (real_location->locked()) {
4122 if (copy_location->is_mark()) {
4126 copy_location->set_start (copy_location->start() + f_delta);
4130 framepos_t new_start = copy_location->start() + f_delta;
4131 framepos_t new_end = copy_location->end() + f_delta;
4133 if (is_start) { // start-of-range marker
4135 if (move_both || (*x).move_both) {
4136 copy_location->set_start (new_start);
4137 copy_location->set_end (new_end);
4138 } else if (new_start < copy_location->end()) {
4139 copy_location->set_start (new_start);
4140 } else if (newframe > 0) {
4141 //_editor->snap_to (next, RoundUpAlways, true);
4142 copy_location->set_end (next);
4143 copy_location->set_start (newframe);
4146 } else { // end marker
4148 if (move_both || (*x).move_both) {
4149 copy_location->set_end (new_end);
4150 copy_location->set_start (new_start);
4151 } else if (new_end > copy_location->start()) {
4152 copy_location->set_end (new_end);
4153 } else if (newframe > 0) {
4154 //_editor->snap_to (next, RoundDownAlways, true);
4155 copy_location->set_start (next);
4156 copy_location->set_end (newframe);
4161 update_item (copy_location);
4163 /* now lookup the actual GUI items used to display this
4164 * location and move them to wherever the copy of the location
4165 * is now. This means that the logic in ARDOUR::Location is
4166 * still enforced, even though we are not (yet) modifying
4167 * the real Location itself.
4170 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4173 lm->set_position (copy_location->start(), copy_location->end());
4178 assert (!_copied_locations.empty());
4180 show_verbose_cursor_time (newframe);
4184 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4186 if (!movement_occurred) {
4188 if (was_double_click()) {
4189 _editor->rename_marker (_marker);
4193 /* just a click, do nothing but finish
4194 off the selection process
4197 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4199 case Selection::Set:
4200 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4201 _editor->selection->set (_marker);
4202 _selection_changed = true;
4206 case Selection::Toggle:
4207 /* we toggle on the button release, click only */
4208 _editor->selection->toggle (_marker);
4209 _selection_changed = true;
4213 case Selection::Extend:
4214 case Selection::Add:
4218 if (_selection_changed) {
4219 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4220 _editor->commit_reversible_selection_op();
4226 _editor->_dragging_edit_point = false;
4228 XMLNode &before = _editor->session()->locations()->get_state();
4229 bool in_command = false;
4231 MarkerSelection::iterator i;
4232 CopiedLocationInfo::iterator x;
4235 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4236 x != _copied_locations.end() && i != _editor->selection->markers.end();
4239 Location * location = _editor->find_location_from_marker (*i, is_start);
4243 if (location->locked()) {
4247 _editor->begin_reversible_command ( _("move marker") );
4250 if (location->is_mark()) {
4251 location->set_start (((*x).location)->start());
4253 location->set (((*x).location)->start(), ((*x).location)->end());
4259 XMLNode &after = _editor->session()->locations()->get_state();
4260 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4261 _editor->commit_reversible_command ();
4266 MarkerDrag::aborted (bool movement_occurred)
4268 if (!movement_occurred) {
4272 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4274 /* move all markers to their original location */
4277 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4280 Location * location = _editor->find_location_from_marker (*m, is_start);
4283 (*m)->set_position (is_start ? location->start() : location->end());
4290 MarkerDrag::update_item (Location*)
4295 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4297 , _fixed_grab_x (0.0)
4298 , _fixed_grab_y (0.0)
4299 , _cumulative_x_drag (0.0)
4300 , _cumulative_y_drag (0.0)
4304 if (_zero_gain_fraction < 0.0) {
4305 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4308 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4310 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4316 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4318 Drag::start_grab (event, _editor->cursors()->fader);
4320 // start the grab at the center of the control point so
4321 // the point doesn't 'jump' to the mouse after the first drag
4322 _fixed_grab_x = _point->get_x();
4323 _fixed_grab_y = _point->get_y();
4325 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4326 setup_snap_delta (pos);
4328 float const fraction = 1 - (_point->get_y() / _point->line().height());
4329 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4331 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4333 if (!_point->can_slide ()) {
4334 _x_constrained = true;
4339 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4341 double dx = _drags->current_pointer_x() - last_pointer_x();
4342 double dy = current_pointer_y() - last_pointer_y();
4343 bool need_snap = true;
4345 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4351 /* coordinate in pixels relative to the start of the region (for region-based automation)
4352 or track (for track-based automation) */
4353 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4354 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4356 // calculate zero crossing point. back off by .01 to stay on the
4357 // positive side of zero
4358 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4360 if (_x_constrained) {
4363 if (_y_constrained) {
4367 _cumulative_x_drag = cx - _fixed_grab_x;
4368 _cumulative_y_drag = cy - _fixed_grab_y;
4372 cy = min ((double) _point->line().height(), cy);
4374 // make sure we hit zero when passing through
4375 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4379 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4381 if (!_x_constrained && need_snap) {
4382 _editor->snap_to_with_modifier (cx_frames, event);
4385 cx_frames -= snap_delta (event->button.state);
4386 cx_frames = min (cx_frames, _point->line().maximum_time());
4388 float const fraction = 1.0 - (cy / _point->line().height());
4391 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4392 _editor->begin_reversible_command (_("automation event move"));
4393 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4395 pair<double, float> result;
4396 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4398 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4402 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4404 if (!movement_occurred) {
4407 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4408 _editor->reset_point_selection ();
4412 _point->line().end_drag (_pushing, _final_index);
4413 _editor->commit_reversible_command ();
4418 ControlPointDrag::aborted (bool)
4420 _point->line().reset ();
4424 ControlPointDrag::active (Editing::MouseMode m)
4426 if (m == Editing::MouseDraw) {
4427 /* always active in mouse draw */
4431 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4432 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4435 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4438 , _fixed_grab_x (0.0)
4439 , _fixed_grab_y (0.0)
4440 , _cumulative_y_drag (0)
4444 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4448 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4450 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4453 _item = &_line->grab_item ();
4455 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4456 origin, and ditto for y.
4459 double mx = event->button.x;
4460 double my = event->button.y;
4462 _line->grab_item().canvas_to_item (mx, my);
4464 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4466 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4467 /* no adjacent points */
4471 Drag::start_grab (event, _editor->cursors()->fader);
4473 /* store grab start in item frame */
4474 double const bx = _line->nth (_before)->get_x();
4475 double const ax = _line->nth (_after)->get_x();
4476 double const click_ratio = (ax - mx) / (ax - bx);
4478 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4483 double fraction = 1.0 - (cy / _line->height());
4485 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4489 LineDrag::motion (GdkEvent* event, bool first_move)
4491 double dy = current_pointer_y() - last_pointer_y();
4493 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4497 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4499 _cumulative_y_drag = cy - _fixed_grab_y;
4502 cy = min ((double) _line->height(), cy);
4504 double const fraction = 1.0 - (cy / _line->height());
4508 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4510 _editor->begin_reversible_command (_("automation range move"));
4511 _line->start_drag_line (_before, _after, initial_fraction);
4514 /* we are ignoring x position for this drag, so we can just pass in anything */
4515 pair<double, float> result;
4517 result = _line->drag_motion (0, fraction, true, false, ignored);
4518 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4522 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4524 if (movement_occurred) {
4525 motion (event, false);
4526 _line->end_drag (false, 0);
4527 _editor->commit_reversible_command ();
4529 /* add a new control point on the line */
4531 AutomationTimeAxisView* atv;
4533 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4534 framepos_t where = grab_frame ();
4537 double cy = _fixed_grab_y;
4539 _line->grab_item().item_to_canvas (cx, cy);
4541 atv->add_automation_event (event, where, cy, false);
4542 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4543 AudioRegionView* arv;
4545 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4546 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4553 LineDrag::aborted (bool)
4558 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4562 _region_view_grab_x (0.0),
4563 _cumulative_x_drag (0),
4567 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4571 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4573 Drag::start_grab (event);
4575 _line = reinterpret_cast<Line*> (_item);
4578 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4580 double cx = event->button.x;
4581 double cy = event->button.y;
4583 _item->parent()->canvas_to_item (cx, cy);
4585 /* store grab start in parent frame */
4586 _region_view_grab_x = cx;
4588 _before = *(float*) _item->get_data ("position");
4590 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4592 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4596 FeatureLineDrag::motion (GdkEvent*, bool)
4598 double dx = _drags->current_pointer_x() - last_pointer_x();
4600 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4602 _cumulative_x_drag += dx;
4604 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4613 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4615 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4617 float *pos = new float;
4620 _line->set_data ("position", pos);
4626 FeatureLineDrag::finished (GdkEvent*, bool)
4628 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4629 _arv->update_transient(_before, _before);
4633 FeatureLineDrag::aborted (bool)
4638 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4640 , _vertical_only (false)
4642 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4646 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4648 Drag::start_grab (event);
4649 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4653 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4660 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4662 framepos_t grab = grab_frame ();
4663 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4664 _editor->snap_to_with_modifier (grab, event);
4666 grab = raw_grab_frame ();
4669 /* base start and end on initial click position */
4679 if (current_pointer_y() < grab_y()) {
4680 y1 = current_pointer_y();
4683 y2 = current_pointer_y();
4687 if (start != end || y1 != y2) {
4689 double x1 = _editor->sample_to_pixel (start);
4690 double x2 = _editor->sample_to_pixel (end);
4691 const double min_dimension = 2.0;
4693 if (_vertical_only) {
4694 /* fixed 10 pixel width */
4698 x2 = min (x1 - min_dimension, x2);
4700 x2 = max (x1 + min_dimension, x2);
4705 y2 = min (y1 - min_dimension, y2);
4707 y2 = max (y1 + min_dimension, y2);
4710 /* translate rect into item space and set */
4712 ArdourCanvas::Rect r (x1, y1, x2, y2);
4714 /* this drag is a _trackview_only == true drag, so the y1 and
4715 * y2 (computed using current_pointer_y() and grab_y()) will be
4716 * relative to the top of the trackview group). The
4717 * rubberband rect has the same parent/scroll offset as the
4718 * the trackview group, so we can use the "r" rect directly
4719 * to set the shape of the rubberband.
4722 _editor->rubberband_rect->set (r);
4723 _editor->rubberband_rect->show();
4724 _editor->rubberband_rect->raise_to_top();
4726 show_verbose_cursor_time (pf);
4728 do_select_things (event, true);
4733 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4737 framepos_t grab = grab_frame ();
4738 framepos_t lpf = last_pointer_frame ();
4740 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4741 grab = raw_grab_frame ();
4742 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4756 if (current_pointer_y() < grab_y()) {
4757 y1 = current_pointer_y();
4760 y2 = current_pointer_y();
4764 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4768 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4770 if (movement_occurred) {
4772 motion (event, false);
4773 do_select_things (event, false);
4779 bool do_deselect = true;
4780 MidiTimeAxisView* mtv;
4782 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4784 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4785 /* nothing selected */
4786 add_midi_region (mtv, true);
4787 do_deselect = false;
4791 /* do not deselect if Primary or Tertiary (toggle-select or
4792 * extend-select are pressed.
4795 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4796 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4803 _editor->rubberband_rect->hide();
4807 RubberbandSelectDrag::aborted (bool)
4809 _editor->rubberband_rect->hide ();
4812 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4813 : RegionDrag (e, i, p, v)
4815 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4819 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4821 Drag::start_grab (event, cursor);
4823 _editor->get_selection().add (_primary);
4825 framepos_t where = _primary->region()->position();
4826 setup_snap_delta (where);
4828 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4832 TimeFXDrag::motion (GdkEvent* event, bool)
4834 RegionView* rv = _primary;
4835 StreamView* cv = rv->get_time_axis_view().view ();
4837 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4838 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4839 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4840 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4841 _editor->snap_to_with_modifier (pf, event);
4842 pf -= snap_delta (event->button.state);
4844 if (pf > rv->region()->position()) {
4845 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4848 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4852 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4854 /* this may have been a single click, no drag. We still want the dialog
4855 to show up in that case, so that the user can manually edit the
4856 parameters for the timestretch.
4859 float fraction = 1.0;
4861 if (movement_occurred) {
4863 motion (event, false);
4865 _primary->get_time_axis_view().hide_timestretch ();
4867 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4869 if (adjusted_frame_pos < _primary->region()->position()) {
4870 /* backwards drag of the left edge - not usable */
4874 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4876 fraction = (double) newlen / (double) _primary->region()->length();
4878 #ifndef USE_RUBBERBAND
4879 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4880 if (_primary->region()->data_type() == DataType::AUDIO) {
4881 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4886 if (!_editor->get_selection().regions.empty()) {
4887 /* primary will already be included in the selection, and edit
4888 group shared editing will propagate selection across
4889 equivalent regions, so just use the current region
4893 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4894 error << _("An error occurred while executing time stretch operation") << endmsg;
4900 TimeFXDrag::aborted (bool)
4902 _primary->get_time_axis_view().hide_timestretch ();
4905 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4908 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4912 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4914 Drag::start_grab (event);
4918 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4920 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4924 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4926 if (movement_occurred && _editor->session()) {
4927 /* make sure we stop */
4928 _editor->session()->request_transport_speed (0.0);
4933 ScrubDrag::aborted (bool)
4938 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4942 , _time_selection_at_start (!_editor->get_selection().time.empty())
4944 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4946 if (_time_selection_at_start) {
4947 start_at_start = _editor->get_selection().time.start();
4948 end_at_start = _editor->get_selection().time.end_frame();
4953 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4955 if (_editor->session() == 0) {
4959 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4961 switch (_operation) {
4962 case CreateSelection:
4963 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4968 cursor = _editor->cursors()->selector;
4969 Drag::start_grab (event, cursor);
4972 case SelectionStartTrim:
4973 if (_editor->clicked_axisview) {
4974 _editor->clicked_axisview->order_selection_trims (_item, true);
4976 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4979 case SelectionEndTrim:
4980 if (_editor->clicked_axisview) {
4981 _editor->clicked_axisview->order_selection_trims (_item, false);
4983 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4987 Drag::start_grab (event, cursor);
4990 case SelectionExtend:
4991 Drag::start_grab (event, cursor);
4995 if (_operation == SelectionMove) {
4996 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4998 show_verbose_cursor_time (adjusted_current_frame (event));
5003 SelectionDrag::setup_pointer_frame_offset ()
5005 switch (_operation) {
5006 case CreateSelection:
5007 _pointer_frame_offset = 0;
5010 case SelectionStartTrim:
5012 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5015 case SelectionEndTrim:
5016 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5019 case SelectionExtend:
5025 SelectionDrag::motion (GdkEvent* event, bool first_move)
5027 framepos_t start = 0;
5029 framecnt_t length = 0;
5030 framecnt_t distance = 0;
5032 framepos_t const pending_position = adjusted_current_frame (event);
5034 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5038 switch (_operation) {
5039 case CreateSelection:
5041 framepos_t grab = grab_frame ();
5044 grab = adjusted_current_frame (event, false);
5045 if (grab < pending_position) {
5046 _editor->snap_to (grab, RoundDownMaybe);
5048 _editor->snap_to (grab, RoundUpMaybe);
5052 if (pending_position < grab) {
5053 start = pending_position;
5056 end = pending_position;
5060 /* first drag: Either add to the selection
5061 or create a new selection
5068 /* adding to the selection */
5069 _editor->set_selected_track_as_side_effect (Selection::Add);
5070 _editor->clicked_selection = _editor->selection->add (start, end);
5077 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5078 _editor->set_selected_track_as_side_effect (Selection::Set);
5081 _editor->clicked_selection = _editor->selection->set (start, end);
5085 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5086 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5087 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5089 _editor->selection->add (atest);
5093 /* select all tracks within the rectangle that we've marked out so far */
5094 TrackViewList new_selection;
5095 TrackViewList& all_tracks (_editor->track_views);
5097 ArdourCanvas::Coord const top = grab_y();
5098 ArdourCanvas::Coord const bottom = current_pointer_y();
5100 if (top >= 0 && bottom >= 0) {
5102 //first, find the tracks that are covered in the y range selection
5103 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5104 if ((*i)->covered_by_y_range (top, bottom)) {
5105 new_selection.push_back (*i);
5109 //now find any tracks that are GROUPED with the tracks we selected
5110 TrackViewList grouped_add = new_selection;
5111 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5112 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5113 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
5114 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5115 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5116 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5117 grouped_add.push_back (*j);
5122 //now compare our list with the current selection, and add or remove as necessary
5123 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5124 TrackViewList tracks_to_add;
5125 TrackViewList tracks_to_remove;
5126 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5127 if ( !_editor->selection->tracks.contains ( *i ) )
5128 tracks_to_add.push_back ( *i );
5129 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5130 if ( !grouped_add.contains ( *i ) )
5131 tracks_to_remove.push_back ( *i );
5132 _editor->selection->add(tracks_to_add);
5133 _editor->selection->remove(tracks_to_remove);
5139 case SelectionStartTrim:
5141 end = _editor->selection->time[_editor->clicked_selection].end;
5143 if (pending_position > end) {
5146 start = pending_position;
5150 case SelectionEndTrim:
5152 start = _editor->selection->time[_editor->clicked_selection].start;
5154 if (pending_position < start) {
5157 end = pending_position;
5164 start = _editor->selection->time[_editor->clicked_selection].start;
5165 end = _editor->selection->time[_editor->clicked_selection].end;
5167 length = end - start;
5168 distance = pending_position - start;
5169 start = pending_position;
5170 _editor->snap_to (start);
5172 end = start + length;
5176 case SelectionExtend:
5181 switch (_operation) {
5183 if (_time_selection_at_start) {
5184 _editor->selection->move_time (distance);
5188 _editor->selection->replace (_editor->clicked_selection, start, end);
5192 if (_operation == SelectionMove) {
5193 show_verbose_cursor_time(start);
5195 show_verbose_cursor_time(pending_position);
5200 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5202 Session* s = _editor->session();
5204 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5205 if (movement_occurred) {
5206 motion (event, false);
5207 /* XXX this is not object-oriented programming at all. ick */
5208 if (_editor->selection->time.consolidate()) {
5209 _editor->selection->TimeChanged ();
5212 /* XXX what if its a music time selection? */
5214 if (s->get_play_range() && s->transport_rolling()) {
5215 s->request_play_range (&_editor->selection->time, true);
5216 } else if (!s->config.get_external_sync()) {
5217 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5218 if (_operation == SelectionEndTrim)
5219 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5221 s->request_locate (_editor->get_selection().time.start());
5225 if (_editor->get_selection().time.length() != 0) {
5226 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5228 s->clear_range_selection ();
5233 /* just a click, no pointer movement.
5236 if (_operation == SelectionExtend) {
5237 if (_time_selection_at_start) {
5238 framepos_t pos = adjusted_current_frame (event, false);
5239 framepos_t start = min (pos, start_at_start);
5240 framepos_t end = max (pos, end_at_start);
5241 _editor->selection->set (start, end);
5244 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5245 if (_editor->clicked_selection) {
5246 _editor->selection->remove (_editor->clicked_selection);
5249 if (!_editor->clicked_selection) {
5250 _editor->selection->clear_time();
5255 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5256 _editor->selection->set (_editor->clicked_axisview);
5259 if (s && s->get_play_range () && s->transport_rolling()) {
5260 s->request_stop (false, false);
5265 _editor->stop_canvas_autoscroll ();
5266 _editor->clicked_selection = 0;
5267 _editor->commit_reversible_selection_op ();
5271 SelectionDrag::aborted (bool)
5276 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5277 : Drag (e, i, false),
5281 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5283 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5284 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5285 physical_screen_height (_editor->current_toplevel()->get_window())));
5286 _drag_rect->hide ();
5288 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5289 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5292 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5294 /* normal canvas items will be cleaned up when their parent group is deleted. But
5295 this item is created as the child of a long-lived parent group, and so we
5296 need to explicitly delete it.
5302 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5304 if (_editor->session() == 0) {
5308 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5310 if (!_editor->temp_location) {
5311 _editor->temp_location = new Location (*_editor->session());
5314 switch (_operation) {
5315 case CreateSkipMarker:
5316 case CreateRangeMarker:
5317 case CreateTransportMarker:
5318 case CreateCDMarker:
5320 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5325 cursor = _editor->cursors()->selector;
5329 Drag::start_grab (event, cursor);
5331 show_verbose_cursor_time (adjusted_current_frame (event));
5335 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5337 framepos_t start = 0;
5339 ArdourCanvas::Rectangle *crect;
5341 switch (_operation) {
5342 case CreateSkipMarker:
5343 crect = _editor->range_bar_drag_rect;
5345 case CreateRangeMarker:
5346 crect = _editor->range_bar_drag_rect;
5348 case CreateTransportMarker:
5349 crect = _editor->transport_bar_drag_rect;
5351 case CreateCDMarker:
5352 crect = _editor->cd_marker_bar_drag_rect;
5355 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5360 framepos_t const pf = adjusted_current_frame (event);
5362 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5363 framepos_t grab = grab_frame ();
5364 _editor->snap_to (grab);
5366 if (pf < grab_frame()) {
5374 /* first drag: Either add to the selection
5375 or create a new selection.
5380 _editor->temp_location->set (start, end);
5384 update_item (_editor->temp_location);
5386 //_drag_rect->raise_to_top();
5392 _editor->temp_location->set (start, end);
5394 double x1 = _editor->sample_to_pixel (start);
5395 double x2 = _editor->sample_to_pixel (end);
5399 update_item (_editor->temp_location);
5402 show_verbose_cursor_time (pf);
5407 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5409 Location * newloc = 0;
5413 if (movement_occurred) {
5414 motion (event, false);
5417 switch (_operation) {
5418 case CreateSkipMarker:
5419 case CreateRangeMarker:
5420 case CreateCDMarker:
5422 XMLNode &before = _editor->session()->locations()->get_state();
5423 if (_operation == CreateSkipMarker) {
5424 _editor->begin_reversible_command (_("new skip marker"));
5425 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5426 flags = Location::IsRangeMarker | Location::IsSkip;
5427 _editor->range_bar_drag_rect->hide();
5428 } else if (_operation == CreateCDMarker) {
5429 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5430 _editor->begin_reversible_command (_("new CD marker"));
5431 flags = Location::IsRangeMarker | Location::IsCDMarker;
5432 _editor->cd_marker_bar_drag_rect->hide();
5434 _editor->begin_reversible_command (_("new skip marker"));
5435 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5436 flags = Location::IsRangeMarker;
5437 _editor->range_bar_drag_rect->hide();
5439 newloc = new Location (
5440 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5443 _editor->session()->locations()->add (newloc, true);
5444 XMLNode &after = _editor->session()->locations()->get_state();
5445 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5446 _editor->commit_reversible_command ();
5450 case CreateTransportMarker:
5451 // popup menu to pick loop or punch
5452 _editor->new_transport_marker_context_menu (&event->button, _item);
5458 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5460 if (_operation == CreateTransportMarker) {
5462 /* didn't drag, so just locate */
5464 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5466 } else if (_operation == CreateCDMarker) {
5468 /* didn't drag, but mark is already created so do
5471 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5476 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5478 if (end == max_framepos) {
5479 end = _editor->session()->current_end_frame ();
5482 if (start == max_framepos) {
5483 start = _editor->session()->current_start_frame ();
5486 switch (_editor->mouse_mode) {
5488 /* find the two markers on either side and then make the selection from it */
5489 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5493 /* find the two markers on either side of the click and make the range out of it */
5494 _editor->selection->set (start, end);
5503 _editor->stop_canvas_autoscroll ();
5507 RangeMarkerBarDrag::aborted (bool movement_occurred)
5509 if (movement_occurred) {
5510 _drag_rect->hide ();
5515 RangeMarkerBarDrag::update_item (Location* location)
5517 double const x1 = _editor->sample_to_pixel (location->start());
5518 double const x2 = _editor->sample_to_pixel (location->end());
5520 _drag_rect->set_x0 (x1);
5521 _drag_rect->set_x1 (x2);
5524 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5526 , _cumulative_dx (0)
5527 , _cumulative_dy (0)
5528 , _was_selected (false)
5530 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5532 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5534 _region = &_primary->region_view ();
5535 _note_height = _region->midi_stream_view()->note_height ();
5539 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5541 Drag::start_grab (event);
5542 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5544 if (!(_was_selected = _primary->selected())) {
5546 /* tertiary-click means extend selection - we'll do that on button release,
5547 so don't add it here, because otherwise we make it hard to figure
5548 out the "extend-to" range.
5551 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5554 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5557 _region->note_selected (_primary, true);
5559 _editor->get_selection().clear_points();
5560 _region->unique_select (_primary);
5566 /** @return Current total drag x change in frames */
5568 NoteDrag::total_dx (const guint state) const
5571 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5573 /* primary note time */
5574 frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5576 /* new time of the primary note in session frames */
5577 frameoffset_t st = n + dx + snap_delta (state);
5579 framepos_t const rp = _region->region()->position ();
5581 /* prevent the note being dragged earlier than the region's position */
5584 /* possibly snap and return corresponding delta */
5588 if (ArdourKeyboard::indicates_snap (state)) {
5589 if (_editor->snap_mode () != SnapOff) {
5593 if (_editor->snap_mode () == SnapOff) {
5595 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5596 if (ArdourKeyboard::indicates_snap_delta (state)) {
5604 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5605 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5607 ret = st - n - snap_delta (state);
5612 /** @return Current total drag y change in note number */
5614 NoteDrag::total_dy () const
5616 MidiStreamView* msv = _region->midi_stream_view ();
5617 double const y = _region->midi_view()->y_position ();
5618 /* new current note */
5619 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5621 n = max (msv->lowest_note(), n);
5622 n = min (msv->highest_note(), n);
5623 /* and work out delta */
5624 return n - msv->y_to_note (grab_y() - y);
5628 NoteDrag::motion (GdkEvent * event, bool)
5630 /* Total change in x and y since the start of the drag */
5631 frameoffset_t const dx = total_dx (event->button.state);
5632 int8_t const dy = total_dy ();
5634 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5635 double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5636 double const tdy = -dy * _note_height - _cumulative_dy;
5639 _cumulative_dx += tdx;
5640 _cumulative_dy += tdy;
5642 int8_t note_delta = total_dy();
5644 _region->move_selection (tdx, tdy, note_delta);
5646 /* the new note value may be the same as the old one, but we
5647 * don't know what that means because the selection may have
5648 * involved more than one note and we might be doing something
5649 * odd with them. so show the note value anyway, always.
5652 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5654 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5659 NoteDrag::finished (GdkEvent* ev, bool moved)
5662 /* no motion - select note */
5664 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5665 _editor->current_mouse_mode() == Editing::MouseDraw) {
5667 bool changed = false;
5669 if (_was_selected) {
5670 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5672 _region->note_deselected (_primary);
5675 _editor->get_selection().clear_points();
5676 _region->unique_select (_primary);
5680 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5681 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5683 if (!extend && !add && _region->selection_size() > 1) {
5684 _editor->get_selection().clear_points();
5685 _region->unique_select (_primary);
5687 } else if (extend) {
5688 _region->note_selected (_primary, true, true);
5691 /* it was added during button press */
5698 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5699 _editor->commit_reversible_selection_op();
5703 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5708 NoteDrag::aborted (bool)
5713 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5714 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5715 : Drag (editor, atv->base_item ())
5717 , _y_origin (atv->y_position())
5718 , _nothing_to_drag (false)
5720 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5721 setup (atv->lines ());
5724 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5725 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5726 : Drag (editor, rv->get_canvas_group ())
5728 , _y_origin (rv->get_time_axis_view().y_position())
5729 , _nothing_to_drag (false)
5732 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5734 list<boost::shared_ptr<AutomationLine> > lines;
5736 AudioRegionView* audio_view;
5737 AutomationRegionView* automation_view;
5738 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5739 lines.push_back (audio_view->get_gain_line ());
5740 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5741 lines.push_back (automation_view->line ());
5744 error << _("Automation range drag created for invalid region type") << endmsg;
5750 /** @param lines AutomationLines to drag.
5751 * @param offset Offset from the session start to the points in the AutomationLines.
5754 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5756 /* find the lines that overlap the ranges being dragged */
5757 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5758 while (i != lines.end ()) {
5759 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5762 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5764 /* check this range against all the AudioRanges that we are using */
5765 list<AudioRange>::const_iterator k = _ranges.begin ();
5766 while (k != _ranges.end()) {
5767 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5773 /* add it to our list if it overlaps at all */
5774 if (k != _ranges.end()) {
5779 _lines.push_back (n);
5785 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5789 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5791 return 1.0 - ((global_y - _y_origin) / line->height());
5795 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5797 const double v = list->eval(x);
5798 return _integral ? rint(v) : v;
5802 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5804 Drag::start_grab (event, cursor);
5806 /* Get line states before we start changing things */
5807 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5808 i->state = &i->line->get_state ();
5809 i->original_fraction = y_fraction (i->line, current_pointer_y());
5812 if (_ranges.empty()) {
5814 /* No selected time ranges: drag all points */
5815 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5816 uint32_t const N = i->line->npoints ();
5817 for (uint32_t j = 0; j < N; ++j) {
5818 i->points.push_back (i->line->nth (j));
5824 if (_nothing_to_drag) {
5830 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5832 if (_nothing_to_drag && !first_move) {
5837 _editor->begin_reversible_command (_("automation range move"));
5839 if (!_ranges.empty()) {
5841 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5843 framecnt_t const half = (i->start + i->end) / 2;
5845 /* find the line that this audio range starts in */
5846 list<Line>::iterator j = _lines.begin();
5847 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5851 if (j != _lines.end()) {
5852 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5854 /* j is the line that this audio range starts in; fade into it;
5855 64 samples length plucked out of thin air.
5858 framepos_t a = i->start + 64;
5863 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5864 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5866 XMLNode &before = the_list->get_state();
5867 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5868 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5870 if (add_p || add_q) {
5871 _editor->session()->add_command (
5872 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5876 /* same thing for the end */
5879 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5883 if (j != _lines.end()) {
5884 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5886 /* j is the line that this audio range starts in; fade out of it;
5887 64 samples length plucked out of thin air.
5890 framepos_t b = i->end - 64;
5895 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5896 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5898 XMLNode &before = the_list->get_state();
5899 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5900 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5902 if (add_p || add_q) {
5903 _editor->session()->add_command (
5904 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5909 _nothing_to_drag = true;
5911 /* Find all the points that should be dragged and put them in the relevant
5912 points lists in the Line structs.
5915 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5917 uint32_t const N = i->line->npoints ();
5918 for (uint32_t j = 0; j < N; ++j) {
5920 /* here's a control point on this line */
5921 ControlPoint* p = i->line->nth (j);
5922 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5924 /* see if it's inside a range */
5925 list<AudioRange>::const_iterator k = _ranges.begin ();
5926 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5930 if (k != _ranges.end()) {
5931 /* dragging this point */
5932 _nothing_to_drag = false;
5933 i->points.push_back (p);
5939 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5940 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5944 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5945 float const f = y_fraction (l->line, current_pointer_y());
5946 /* we are ignoring x position for this drag, so we can just pass in anything */
5947 pair<double, float> result;
5949 result = l->line->drag_motion (0, f, true, false, ignored);
5950 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5955 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5957 if (_nothing_to_drag || !motion_occurred) {
5961 motion (event, false);
5962 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5963 i->line->end_drag (false, 0);
5966 _editor->commit_reversible_command ();
5970 AutomationRangeDrag::aborted (bool)
5972 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5977 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5979 , initial_time_axis_view (itav)
5981 /* note that time_axis_view may be null if the regionview was created
5982 * as part of a copy operation.
5984 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5985 layer = v->region()->layer ();
5986 initial_y = v->get_canvas_group()->position().y;
5987 initial_playlist = v->region()->playlist ();
5988 initial_position = v->region()->position ();
5989 initial_end = v->region()->position () + v->region()->length ();
5992 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5993 : Drag (e, i->canvas_item ())
5996 , _cumulative_dx (0)
5998 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5999 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6004 PatchChangeDrag::motion (GdkEvent* ev, bool)
6006 framepos_t f = adjusted_current_frame (ev);
6007 boost::shared_ptr<Region> r = _region_view->region ();
6008 f = max (f, r->position ());
6009 f = min (f, r->last_frame ());
6011 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6012 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6013 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6014 _cumulative_dx = dxu;
6018 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6020 if (!movement_occurred) {
6021 if (was_double_click()) {
6022 _region_view->edit_patch_change (_patch_change);
6027 boost::shared_ptr<Region> r (_region_view->region ());
6028 framepos_t f = adjusted_current_frame (ev);
6029 f = max (f, r->position ());
6030 f = min (f, r->last_frame ());
6032 _region_view->move_patch_change (
6034 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6039 PatchChangeDrag::aborted (bool)
6041 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6045 PatchChangeDrag::setup_pointer_frame_offset ()
6047 boost::shared_ptr<Region> region = _region_view->region ();
6048 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6051 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6052 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6059 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6061 _region_view->update_drag_selection (
6063 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6067 MidiRubberbandSelectDrag::deselect_things ()
6072 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6073 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6076 _vertical_only = true;
6080 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6082 double const y = _region_view->midi_view()->y_position ();
6084 y1 = max (0.0, y1 - y);
6085 y2 = max (0.0, y2 - y);
6087 _region_view->update_vertical_drag_selection (
6090 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6095 MidiVerticalSelectDrag::deselect_things ()
6100 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6101 : RubberbandSelectDrag (e, i)
6107 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6109 if (drag_in_progress) {
6110 /* We just want to select things at the end of the drag, not during it */
6114 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6116 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6118 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6120 _editor->commit_reversible_selection_op ();
6124 EditorRubberbandSelectDrag::deselect_things ()
6126 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6128 _editor->selection->clear_tracks();
6129 _editor->selection->clear_regions();
6130 _editor->selection->clear_points ();
6131 _editor->selection->clear_lines ();
6132 _editor->selection->clear_midi_notes ();
6134 _editor->commit_reversible_selection_op();
6137 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6142 _note[0] = _note[1] = 0;
6145 NoteCreateDrag::~NoteCreateDrag ()
6151 NoteCreateDrag::grid_frames (framepos_t t) const
6154 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6156 grid_beats = Evoral::Beats(1);
6159 return _region_view->region_beats_to_region_frames (grid_beats);
6163 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6165 Drag::start_grab (event, cursor);
6167 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6169 framepos_t pf = _drags->current_pointer_frame ();
6170 framecnt_t const g = grid_frames (pf);
6172 /* Hack so that we always snap to the note that we are over, instead of snapping
6173 to the next one if we're more than halfway through the one we're over.
6175 if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6179 _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6180 _note[1] = _note[0];
6182 MidiStreamView* sv = _region_view->midi_stream_view ();
6183 double const x = _editor->sample_to_pixel (_note[0]);
6184 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6186 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6187 _drag_rect->set_outline_all ();
6188 _drag_rect->set_outline_color (0xffffff99);
6189 _drag_rect->set_fill_color (0xffffff66);
6193 NoteCreateDrag::motion (GdkEvent* event, bool)
6195 _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6196 double const x0 = _editor->sample_to_pixel (_note[0]);
6197 double const x1 = _editor->sample_to_pixel (_note[1]);
6198 _drag_rect->set_x0 (std::min(x0, x1));
6199 _drag_rect->set_x1 (std::max(x0, x1));
6203 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6205 if (!had_movement) {
6209 framepos_t const start = min (_note[0], _note[1]);
6210 framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6212 framecnt_t const g = grid_frames (start);
6213 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6215 if (_editor->snap_mode() == SnapNormal && length < g) {
6219 Evoral::Beats length_beats = max (
6220 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6222 _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6226 NoteCreateDrag::y_to_region (double y) const
6229 _region_view->get_canvas_group()->canvas_to_item (x, y);
6234 NoteCreateDrag::aborted (bool)
6239 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6244 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6248 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6250 Drag::start_grab (event, cursor);
6254 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6260 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6263 distance = _drags->current_pointer_x() - grab_x();
6264 len = ar->fade_in()->back()->when;
6266 distance = grab_x() - _drags->current_pointer_x();
6267 len = ar->fade_out()->back()->when;
6270 /* how long should it be ? */
6272 new_length = len + _editor->pixel_to_sample (distance);
6274 /* now check with the region that this is legal */
6276 new_length = ar->verify_xfade_bounds (new_length, start);
6279 arv->reset_fade_in_shape_width (ar, new_length);
6281 arv->reset_fade_out_shape_width (ar, new_length);
6286 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6292 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6295 distance = _drags->current_pointer_x() - grab_x();
6296 len = ar->fade_in()->back()->when;
6298 distance = grab_x() - _drags->current_pointer_x();
6299 len = ar->fade_out()->back()->when;
6302 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6304 _editor->begin_reversible_command ("xfade trim");
6305 ar->playlist()->clear_owned_changes ();
6308 ar->set_fade_in_length (new_length);
6310 ar->set_fade_out_length (new_length);
6313 /* Adjusting the xfade may affect other regions in the playlist, so we need
6314 to get undo Commands from the whole playlist rather than just the
6318 vector<Command*> cmds;
6319 ar->playlist()->rdiff (cmds);
6320 _editor->session()->add_commands (cmds);
6321 _editor->commit_reversible_command ();
6326 CrossfadeEdgeDrag::aborted (bool)
6329 // arv->redraw_start_xfade ();
6331 // arv->redraw_end_xfade ();
6335 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6336 : Drag (e, item, true)
6337 , line (new EditorCursor (*e))
6339 line->set_position (pos);
6343 RegionCutDrag::~RegionCutDrag ()
6349 RegionCutDrag::motion (GdkEvent*, bool)
6351 framepos_t where = _drags->current_pointer_frame();
6352 _editor->snap_to (where);
6354 line->set_position (where);
6358 RegionCutDrag::finished (GdkEvent*, bool)
6360 _editor->get_track_canvas()->canvas()->re_enter();
6362 framepos_t pos = _drags->current_pointer_frame();
6366 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6372 _editor->split_regions_at (pos, rs);
6376 RegionCutDrag::aborted (bool)