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)
240 , _constraint_pressed (false)
246 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
252 _cursor_ctx = CursorContext::create (*_editor, cursor);
254 _cursor_ctx->change (cursor);
261 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
264 /* we set up x/y dragging constraints on first move */
265 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
267 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
269 setup_pointer_frame_offset ();
270 _grab_frame = adjusted_frame (_raw_grab_frame, event);
271 _last_pointer_frame = _grab_frame;
272 _last_pointer_x = _grab_x;
274 if (_trackview_only) {
275 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
278 _last_pointer_y = _grab_y;
282 if (!_editor->cursors()->is_invalid (cursor)) {
283 /* CAIROCANVAS need a variant here that passes *cursor */
284 _cursor_ctx = CursorContext::create (*_editor, cursor);
287 if (_editor->session() && _editor->session()->transport_rolling()) {
290 _was_rolling = false;
293 switch (_editor->snap_type()) {
294 case SnapToRegionStart:
295 case SnapToRegionEnd:
296 case SnapToRegionSync:
297 case SnapToRegionBoundary:
298 _editor->build_region_boundary_cache ();
305 /** Call to end a drag `successfully'. Ungrabs item and calls
306 * subclass' finished() method.
308 * @param event GDK event, or 0.
309 * @return true if some movement occurred, otherwise false.
312 Drag::end_grab (GdkEvent* event)
314 _editor->stop_canvas_autoscroll ();
318 finished (event, _move_threshold_passed);
320 _editor->verbose_cursor()->hide ();
323 return _move_threshold_passed;
327 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
331 if (f > _pointer_frame_offset) {
332 pos = f - _pointer_frame_offset;
336 _editor->snap_to_with_modifier (pos, event);
343 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
345 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
349 Drag::snap_delta (guint state) const
351 if (ArdourKeyboard::indicates_snap_delta (state)) {
359 Drag::current_pointer_x() const
361 return _drags->current_pointer_x ();
365 Drag::current_pointer_y () const
367 if (!_trackview_only) {
368 return _drags->current_pointer_y ();
371 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
375 Drag::setup_snap_delta (framepos_t pos)
377 framepos_t temp = pos;
378 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
379 _snap_delta = temp - pos;
383 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
385 /* check to see if we have moved in any way that matters since the last motion event */
386 if (_move_threshold_passed &&
387 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
388 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
392 pair<framecnt_t, int> const threshold = move_threshold ();
394 bool const old_move_threshold_passed = _move_threshold_passed;
396 if (!_move_threshold_passed) {
398 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
399 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
401 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
404 if (active (_editor->mouse_mode) && _move_threshold_passed) {
406 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
408 if (old_move_threshold_passed != _move_threshold_passed) {
412 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
413 _initially_vertical = true;
415 _initially_vertical = false;
417 /** check constraints for this drag.
418 * Note that the current convention is to use "contains" for
419 * key modifiers during motion and "equals" when initiating a drag.
420 * In this case we haven't moved yet, so "equals" applies here.
422 if (Config->get_edit_mode() != Lock) {
423 if (event->motion.state & Gdk::BUTTON2_MASK) {
424 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
425 if (_constraint_pressed) {
426 _x_constrained = false;
427 _y_constrained = true;
429 _x_constrained = true;
430 _y_constrained = false;
432 } else if (_constraint_pressed) {
433 // if dragging normally, the motion is constrained to the first direction of movement.
434 if (_initially_vertical) {
435 _x_constrained = true;
436 _y_constrained = false;
438 _x_constrained = false;
439 _y_constrained = true;
443 if (event->button.state & Gdk::BUTTON2_MASK) {
444 _x_constrained = false;
446 _x_constrained = true;
448 _y_constrained = false;
452 if (!from_autoscroll) {
453 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
456 if (!_editor->autoscroll_active() || from_autoscroll) {
459 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
461 motion (event, first_move && !_starting_point_passed);
463 if (first_move && !_starting_point_passed) {
464 _starting_point_passed = true;
467 _last_pointer_x = _drags->current_pointer_x ();
468 _last_pointer_y = current_pointer_y ();
469 _last_pointer_frame = adjusted_current_frame (event, false);
479 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
487 aborted (_move_threshold_passed);
489 _editor->stop_canvas_autoscroll ();
490 _editor->verbose_cursor()->hide ();
494 Drag::show_verbose_cursor_time (framepos_t frame)
496 _editor->verbose_cursor()->set_time (frame);
497 _editor->verbose_cursor()->show ();
501 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
503 _editor->verbose_cursor()->set_duration (start, end);
504 _editor->verbose_cursor()->show ();
508 Drag::show_verbose_cursor_text (string const & text)
510 _editor->verbose_cursor()->set (text);
511 _editor->verbose_cursor()->show ();
514 boost::shared_ptr<Region>
515 Drag::add_midi_region (MidiTimeAxisView* view, bool commit, const int32_t sub_num)
517 if (_editor->session()) {
518 const TempoMap& map (_editor->session()->tempo_map());
519 framecnt_t pos = grab_frame();
520 /* not that the frame rate used here can be affected by pull up/down which
523 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
524 return view->add_region (grab_frame(), len, commit, sub_num);
527 return boost::shared_ptr<Region>();
530 struct PresentationInfoTimeAxisViewSorter {
531 bool operator() (TimeAxisView* a, TimeAxisView* b) {
532 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
536 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
541 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
543 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
544 as some of the regions we are dragging may be on such tracks.
547 TrackViewList track_views = _editor->track_views;
548 track_views.sort (PresentationInfoTimeAxisViewSorter ());
550 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
551 _time_axis_views.push_back (*i);
553 TimeAxisView::Children children_list = (*i)->get_child_list ();
554 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
555 _time_axis_views.push_back (j->get());
559 /* the list of views can be empty at this point if this is a region list-insert drag
562 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
563 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
566 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
570 RegionDrag::region_going_away (RegionView* v)
572 list<DraggingView>::iterator i = _views.begin ();
573 while (i != _views.end() && i->view != v) {
577 if (i != _views.end()) {
582 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
583 * or -1 if it is not found.
586 RegionDrag::find_time_axis_view (TimeAxisView* t) const
589 int const N = _time_axis_views.size ();
590 while (i < N && _time_axis_views[i] != t) {
594 if (_time_axis_views[i] != t) {
601 /** determines if @pos is the closest frame to an exact musical division when using SnapMagnetic.
602 * (SnapMagnetic may snap to an exact division or no division at all).
603 * this is a hotfix for musical region position/trim. we should really
604 * deliver musical divisors when snapping magnetically to avoid this.
607 RegionDrag::current_music_divisor (framepos_t pos, int32_t button_state)
609 int32_t divisions = _editor->get_grid_music_divisions (button_state);
611 if (_editor->snap_mode() == Editing::SnapMagnetic && !ArdourKeyboard::indicates_snap (button_state)) {
612 TempoMap& tmap (_editor->session()->tempo_map());
613 const framepos_t exact_qn_pos = tmap.frame_at_quarter_note (tmap.exact_qn_at_frame (pos, divisions));
615 if (pos != exact_qn_pos) {
616 /* magnetic has not snapped */
624 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
625 : RegionDrag (e, i, p, v)
627 , _ignore_video_lock (false)
629 , _last_pointer_time_axis_view (0)
630 , _last_pointer_layer (0)
635 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
639 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
641 Drag::start_grab (event, cursor);
642 setup_snap_delta (_last_frame_position);
644 show_verbose_cursor_time (_last_frame_position);
646 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
648 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
649 assert(_last_pointer_time_axis_view >= 0);
650 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
653 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
654 _ignore_video_lock = true;
658 /* cross track dragging seems broken here. disabled for now. */
659 _y_constrained = true;
664 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
666 /* compute the amount of pointer motion in frames, and where
667 the region would be if we moved it by that much.
669 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
671 framepos_t sync_frame;
672 framecnt_t sync_offset;
675 sync_offset = _primary->region()->sync_offset (sync_dir);
677 /* we don't handle a sync point that lies before zero.
679 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
681 framecnt_t const sd = snap_delta (event->button.state);
682 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
684 _editor->snap_to_with_modifier (sync_frame, event);
686 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
689 *pending_region_position = _last_frame_position;
692 if (*pending_region_position > max_framepos - _primary->region()->length()) {
693 *pending_region_position = _last_frame_position;
698 bool const x_move_allowed = !_x_constrained;
700 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
702 /* x movement since last time (in pixels) */
703 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
705 /* total x movement */
706 framecnt_t total_dx = *pending_region_position;
707 if (regions_came_from_canvas()) {
708 total_dx = total_dx - grab_frame ();
711 /* check that no regions have gone off the start of the session */
712 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
713 if ((i->view->region()->position() + total_dx) < 0) {
715 *pending_region_position = _last_frame_position;
726 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
732 const int tavsize = _time_axis_views.size();
733 const int dt = delta > 0 ? +1 : -1;
735 int target = start + delta - skip;
737 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
738 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
740 while (current >= 0 && current != target) {
742 if (current < 0 && dt < 0) {
745 if (current >= tavsize && dt > 0) {
748 if (current < 0 || current >= tavsize) {
752 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
753 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
757 if (distance_only && current == start + delta) {
765 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
767 if (_y_constrained) {
771 const int tavsize = _time_axis_views.size();
772 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
773 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
774 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
776 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
777 /* already in the drop zone */
778 if (delta_track >= 0) {
779 /* downward motion - OK if others are still not in the dropzone */
788 } else if (n >= tavsize) {
789 /* downward motion into drop zone. That's fine. */
793 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
794 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
795 /* not a track, or the wrong type */
799 double const l = i->layer + delta_layer;
801 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
802 mode to allow the user to place a region below another on layer 0.
804 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
805 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
806 If it has, the layers will be munged later anyway, so it's ok.
812 /* all regions being dragged are ok with this change */
816 struct DraggingViewSorter {
817 bool operator() (const DraggingView& a, const DraggingView& b) {
818 return a.time_axis_view < b.time_axis_view;
823 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
825 double delta_layer = 0;
826 int delta_time_axis_view = 0;
827 int current_pointer_time_axis_view = -1;
829 assert (!_views.empty ());
831 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
833 /* Find the TimeAxisView that the pointer is now over */
834 const double cur_y = current_pointer_y ();
835 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
836 TimeAxisView* tv = r.first;
838 if (!tv && cur_y < 0) {
839 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
843 /* find drop-zone y-position */
844 Coord last_track_bottom_edge;
845 last_track_bottom_edge = 0;
846 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
847 if (!(*t)->hidden()) {
848 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
853 if (tv && tv->view()) {
854 /* the mouse is over a track */
855 double layer = r.second;
857 if (first_move && tv->view()->layer_display() == Stacked) {
858 tv->view()->set_layer_display (Expanded);
861 /* Here's the current pointer position in terms of time axis view and layer */
862 current_pointer_time_axis_view = find_time_axis_view (tv);
863 assert(current_pointer_time_axis_view >= 0);
865 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
867 /* Work out the change in y */
869 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
870 if (!rtv || !rtv->is_track()) {
871 /* ignore non-tracks early on. we can't move any regions on them */
872 } else if (_last_pointer_time_axis_view < 0) {
873 /* Was in the drop-zone, now over a track.
874 * Hence it must be an upward move (from the bottom)
876 * track_index is still -1, so delta must be set to
877 * move up the correct number of tracks from the bottom.
879 * This is necessary because steps may be skipped if
880 * the bottom-most track is not a valid target and/or
881 * if there are hidden tracks at the bottom.
882 * Hence the initial offset (_ddropzone) as well as the
883 * last valid pointer position (_pdropzone) need to be
884 * taken into account.
886 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
888 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
891 /* TODO needs adjustment per DraggingView,
893 * e.g. select one region on the top-layer of a track
894 * and one region which is at the bottom-layer of another track
897 * Indicated drop-zones and layering is wrong.
898 * and may infer additional layers on the target-track
899 * (depending how many layers the original track had).
901 * Or select two regions (different layers) on a same track,
902 * move across a non-layer track.. -> layering info is lost.
903 * on drop either of the regions may be on top.
905 * Proposed solution: screw it :) well,
906 * don't use delta_layer, use an absolute value
907 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
908 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
909 * 3) iterate over all DraggingView, find the one that is over the track with most layers
910 * 4) proportionally scale layer to layers available on target
912 delta_layer = current_pointer_layer - _last_pointer_layer;
915 /* for automation lanes, there is a TimeAxisView but no ->view()
916 * if (!tv) -> dropzone
918 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
919 /* Moving into the drop-zone.. */
920 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
921 /* delta_time_axis_view may not be sufficient to move into the DZ
922 * the mouse may enter it, but it may not be a valid move due to
925 * -> remember the delta needed to move into the dropzone
927 _ddropzone = delta_time_axis_view;
928 /* ..but subtract hidden tracks (or routes) at the bottom.
929 * we silently move mover them
931 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
932 - _time_axis_views.size();
934 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
935 /* move around inside the zone.
936 * This allows to move further down until all regions are in the zone.
938 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
939 assert(ptr_y >= last_track_bottom_edge);
940 assert(_ddropzone > 0);
942 /* calculate mouse position in 'tracks' below last track. */
943 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
944 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
946 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
948 delta_time_axis_view = dzpos - _pdropzone;
949 } else if (dzpos < _pdropzone && _ndropzone > 0) {
950 // move up inside the DZ
951 delta_time_axis_view = dzpos - _pdropzone;
955 /* Work out the change in x */
956 framepos_t pending_region_position;
957 double const x_delta = compute_x_delta (event, &pending_region_position);
958 _last_frame_position = pending_region_position;
960 /* calculate hidden tracks in current y-axis delta */
962 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
963 /* The mouse is more than one track below the dropzone.
964 * distance calculation is not needed (and would not work, either
965 * because the dropzone is "packed").
967 * Except when [partially] moving regions out of dropzone in a large step.
968 * (the mouse may or may not remain in the DZ)
969 * Hidden tracks at the bottom of the TAV need to be skipped.
971 * This also handles the case if the mouse entered the DZ
972 * in a large step (exessive delta), either due to fast-movement,
973 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
975 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
976 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
978 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
979 -_time_axis_views.size() - dt;
982 else if (_last_pointer_time_axis_view < 0) {
983 /* Moving out of the zone. Check for hidden tracks at the bottom. */
984 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
985 -_time_axis_views.size() - delta_time_axis_view;
987 /* calculate hidden tracks that are skipped by the pointer movement */
988 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
989 - _last_pointer_time_axis_view
990 - delta_time_axis_view;
993 /* Verify change in y */
994 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
995 /* this y movement is not allowed, so do no y movement this time */
996 delta_time_axis_view = 0;
1001 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
1002 /* haven't reached next snap point, and we're not switching
1003 trackviews nor layers. nothing to do.
1008 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
1009 PlaylistDropzoneMap playlist_dropzone_map;
1010 _ndropzone = 0; // number of elements currently in the dropzone
1013 /* sort views by time_axis.
1014 * This retains track order in the dropzone, regardless
1015 * of actual selection order
1017 _views.sort (DraggingViewSorter());
1019 /* count number of distinct tracks of all regions
1020 * being dragged, used for dropzone.
1022 int prev_track = -1;
1023 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1024 if (i->time_axis_view != prev_track) {
1025 prev_track = i->time_axis_view;
1031 _views.back().time_axis_view -
1032 _views.front().time_axis_view;
1034 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1035 - _views.back().time_axis_view;
1037 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1041 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1043 RegionView* rv = i->view;
1048 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1055 /* reparent the regionview into a group above all
1059 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1060 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1061 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1062 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1063 /* move the item so that it continues to appear at the
1064 same location now that its parent has changed.
1066 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1069 /* If we have moved tracks, we'll fudge the layer delta so that the
1070 region gets moved back onto layer 0 on its new track; this avoids
1071 confusion when dragging regions from non-zero layers onto different
1074 double this_delta_layer = delta_layer;
1075 if (delta_time_axis_view != 0) {
1076 this_delta_layer = - i->layer;
1079 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1081 int track_index = i->time_axis_view + this_delta_time_axis_view;
1082 assert(track_index >= 0);
1084 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1085 /* Track is in the Dropzone */
1087 i->time_axis_view = track_index;
1088 assert(i->time_axis_view >= (int) _time_axis_views.size());
1091 double yposition = 0;
1092 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1093 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1096 /* store index of each new playlist as a negative count, starting at -1 */
1098 if (pdz == playlist_dropzone_map.end()) {
1099 /* compute where this new track (which doesn't exist yet) will live
1102 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1104 /* How high is this region view ? */
1106 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1107 ArdourCanvas::Rect bbox;
1110 bbox = obbox.get ();
1113 last_track_bottom_edge += bbox.height();
1115 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1118 yposition = pdz->second;
1121 /* values are zero or negative, hence the use of min() */
1122 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1125 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1127 mrv->apply_note_range (60, 71, true);
1131 /* The TimeAxisView that this region is now over */
1132 TimeAxisView* current_tv = _time_axis_views[track_index];
1134 /* Ensure it is moved from stacked -> expanded if appropriate */
1135 if (current_tv->view()->layer_display() == Stacked) {
1136 current_tv->view()->set_layer_display (Expanded);
1139 /* We're only allowed to go -ve in layer on Expanded views */
1140 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1141 this_delta_layer = - i->layer;
1145 rv->set_height (current_tv->view()->child_height ());
1147 /* Update show/hidden status as the region view may have come from a hidden track,
1148 or have moved to one.
1150 if (current_tv->hidden ()) {
1151 rv->get_canvas_group()->hide ();
1153 rv->get_canvas_group()->show ();
1156 /* Update the DraggingView */
1157 i->time_axis_view = track_index;
1158 i->layer += this_delta_layer;
1161 _editor->mouse_brush_insert_region (rv, pending_region_position);
1165 /* Get the y coordinate of the top of the track that this region is now over */
1166 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1168 /* And adjust for the layer that it should be on */
1169 StreamView* cv = current_tv->view ();
1170 switch (cv->layer_display ()) {
1174 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1177 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1181 /* need to get the parent of the regionview
1182 * canvas group and get its position in
1183 * equivalent coordinate space as the trackview
1184 * we are now dragging over.
1187 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1191 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1193 MidiStreamView* msv;
1194 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1195 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1200 /* Now move the region view */
1201 rv->move (x_delta, y_delta);
1203 } /* foreach region */
1205 _total_x_delta += x_delta;
1207 if (x_delta != 0 && !_brushing) {
1208 show_verbose_cursor_time (_last_frame_position);
1211 /* keep track of pointer movement */
1213 /* the pointer is currently over a time axis view */
1215 if (_last_pointer_time_axis_view < 0) {
1216 /* last motion event was not over a time axis view
1217 * or last y-movement out of the dropzone was not valid
1220 if (delta_time_axis_view < 0) {
1221 /* in the drop zone, moving up */
1223 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1224 * We do not use negative _last_pointer_time_axis_view because
1225 * the dropzone is "packed" (the actual track offset is ignored)
1227 * As opposed to the actual number
1228 * of elements in the dropzone (_ndropzone)
1229 * _pdropzone is not constrained. This is necessary
1230 * to allow moving multiple regions with y-distance
1233 * There can be 0 elements in the dropzone,
1234 * even though the drag-pointer is inside the DZ.
1237 * [ Audio-track, Midi-track, Audio-track, DZ ]
1238 * move regions from both audio tracks at the same time into the
1239 * DZ by grabbing the region in the bottom track.
1241 assert(current_pointer_time_axis_view >= 0);
1242 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1246 /* only move out of the zone if the movement is OK */
1247 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1248 assert(delta_time_axis_view < 0);
1249 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1250 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1251 * the current position can be calculated as follows:
1253 // a well placed oofus attack can still throw this off.
1254 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1255 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1258 /* last motion event was also over a time axis view */
1259 _last_pointer_time_axis_view += delta_time_axis_view;
1260 assert(_last_pointer_time_axis_view >= 0);
1265 /* the pointer is not over a time axis view */
1266 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1267 _pdropzone += delta_time_axis_view - delta_skip;
1268 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1271 _last_pointer_layer += delta_layer;
1275 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1277 if (_copy && first_move) {
1278 if (_x_constrained && !_brushing) {
1279 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1280 } else if (!_brushing) {
1281 _editor->begin_reversible_command (Operations::region_copy);
1282 } else if (_brushing) {
1283 _editor->begin_reversible_command (Operations::drag_region_brush);
1285 /* duplicate the regionview(s) and region(s) */
1287 list<DraggingView> new_regionviews;
1289 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1291 RegionView* rv = i->view;
1292 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1293 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1295 const boost::shared_ptr<const Region> original = rv->region();
1296 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true
1297 , current_music_divisor (original->position(), event->button.state));
1298 /* need to set this so that the drop zone code can work. This doesn't
1299 actually put the region into the playlist, but just sets a weak pointer
1302 region_copy->set_playlist (original->playlist());
1306 boost::shared_ptr<AudioRegion> audioregion_copy
1307 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1309 nrv = new AudioRegionView (*arv, audioregion_copy);
1311 boost::shared_ptr<MidiRegion> midiregion_copy
1312 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1313 nrv = new MidiRegionView (*mrv, midiregion_copy);
1318 nrv->get_canvas_group()->show ();
1319 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1321 /* swap _primary to the copy */
1323 if (rv == _primary) {
1327 /* ..and deselect the one we copied */
1329 rv->set_selected (false);
1332 if (!new_regionviews.empty()) {
1334 /* reflect the fact that we are dragging the copies */
1336 _views = new_regionviews;
1338 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1341 } else if (!_copy && first_move) {
1342 if (_x_constrained && !_brushing) {
1343 _editor->begin_reversible_command (_("fixed time region drag"));
1344 } else if (!_brushing) {
1345 _editor->begin_reversible_command (Operations::region_drag);
1346 } else if (_brushing) {
1347 _editor->begin_reversible_command (Operations::drag_region_brush);
1350 RegionMotionDrag::motion (event, first_move);
1354 RegionMotionDrag::finished (GdkEvent *, bool)
1356 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1357 if (!(*i)->view()) {
1361 if ((*i)->view()->layer_display() == Expanded) {
1362 (*i)->view()->set_layer_display (Stacked);
1368 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1370 RegionMotionDrag::finished (ev, movement_occurred);
1372 if (!movement_occurred) {
1376 if (was_double_click() && !_views.empty()) {
1377 DraggingView dv = _views.front();
1378 dv.view->show_region_editor ();
1385 assert (!_views.empty ());
1387 /* We might have hidden region views so that they weren't visible during the drag
1388 (when they have been reparented). Now everything can be shown again, as region
1389 views are back in their track parent groups.
1391 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1392 i->view->get_canvas_group()->show ();
1395 bool const changed_position = (_last_frame_position != _primary->region()->position());
1396 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1397 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1419 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1423 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1425 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1428 TimeAxisView* tav = 0;
1430 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1431 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1432 uint32_t output_chan = region->n_channels();
1433 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1434 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1436 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1437 tav =_editor->axis_view_from_stripable (audio_tracks.front());
1439 ChanCount one_midi_port (DataType::MIDI, 1);
1440 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1441 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1442 (ARDOUR::Plugin::PresetRecord*) 0,
1443 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1444 tav = _editor->axis_view_from_stripable (midi_tracks.front());
1448 tav->set_height (original->current_height());
1451 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1454 return dynamic_cast<RouteTimeAxisView*> (tav);
1458 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state)
1460 RegionSelection new_views;
1461 PlaylistSet modified_playlists;
1462 RouteTimeAxisView* new_time_axis_view = 0;
1465 /* all changes were made during motion event handlers */
1467 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1471 _editor->commit_reversible_command ();
1475 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1476 PlaylistMapping playlist_mapping;
1478 /* insert the regions into their new playlists */
1479 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1481 RouteTimeAxisView* dest_rtv = 0;
1483 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1489 if (changed_position && !_x_constrained) {
1490 where = i->view->region()->position() - drag_delta;
1492 where = i->view->region()->position();
1495 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1496 /* dragged to drop zone */
1498 PlaylistMapping::iterator pm;
1500 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1501 /* first region from this original playlist: create a new track */
1502 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1503 if(!new_time_axis_view) {
1507 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1508 dest_rtv = new_time_axis_view;
1510 /* we already created a new track for regions from this playlist, use it */
1511 dest_rtv = pm->second;
1514 /* destination time axis view is the one we dragged to */
1515 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1518 if (dest_rtv != 0) {
1519 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1520 modified_playlists, current_music_divisor (where, ev_state));
1522 if (new_view != 0) {
1523 new_views.push_back (new_view);
1527 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1528 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1531 list<DraggingView>::const_iterator next = i;
1537 /* If we've created new regions either by copying or moving
1538 to a new track, we want to replace the old selection with the new ones
1541 if (new_views.size() > 0) {
1542 _editor->selection->set (new_views);
1545 /* write commands for the accumulated diffs for all our modified playlists */
1546 add_stateful_diff_commands_for_playlists (modified_playlists);
1548 _editor->commit_reversible_command ();
1552 RegionMoveDrag::finished_no_copy (
1553 bool const changed_position,
1554 bool const changed_tracks,
1555 framecnt_t const drag_delta,
1556 int32_t const ev_state
1559 RegionSelection new_views;
1560 PlaylistSet modified_playlists;
1561 PlaylistSet frozen_playlists;
1562 set<RouteTimeAxisView*> views_to_update;
1563 RouteTimeAxisView* new_time_axis_view = 0;
1565 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1566 PlaylistMapping playlist_mapping;
1568 std::set<boost::shared_ptr<const Region> > uniq;
1569 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1571 RegionView* rv = i->view;
1572 RouteTimeAxisView* dest_rtv = 0;
1574 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1579 if (uniq.find (rv->region()) != uniq.end()) {
1580 /* prevent duplicate moves when selecting regions from shared playlists */
1584 uniq.insert(rv->region());
1586 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1587 /* dragged to drop zone */
1589 PlaylistMapping::iterator pm;
1591 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1592 /* first region from this original playlist: create a new track */
1593 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1594 if(!new_time_axis_view) { // New track creation failed
1598 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1599 dest_rtv = new_time_axis_view;
1601 /* we already created a new track for regions from this playlist, use it */
1602 dest_rtv = pm->second;
1606 /* destination time axis view is the one we dragged to */
1607 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1612 double const dest_layer = i->layer;
1614 views_to_update.insert (dest_rtv);
1618 if (changed_position && !_x_constrained) {
1619 where = rv->region()->position() - drag_delta;
1621 where = rv->region()->position();
1624 if (changed_tracks) {
1626 /* insert into new playlist */
1628 RegionView* new_view = insert_region_into_playlist (
1629 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1630 modified_playlists, current_music_divisor (where, ev_state)
1633 if (new_view == 0) {
1638 new_views.push_back (new_view);
1640 /* remove from old playlist */
1642 /* the region that used to be in the old playlist is not
1643 moved to the new one - we use a copy of it. as a result,
1644 any existing editor for the region should no longer be
1647 rv->hide_region_editor();
1650 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1654 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1656 /* this movement may result in a crossfade being modified, or a layering change,
1657 so we need to get undo data from the playlist as well as the region.
1660 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1662 playlist->clear_changes ();
1665 rv->region()->clear_changes ();
1668 motion on the same track. plonk the previously reparented region
1669 back to its original canvas group (its streamview).
1670 No need to do anything for copies as they are fake regions which will be deleted.
1673 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1674 rv->get_canvas_group()->set_y_position (i->initial_y);
1677 /* just change the model */
1678 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1679 playlist->set_layer (rv->region(), dest_layer);
1682 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1684 r = frozen_playlists.insert (playlist);
1687 playlist->freeze ();
1690 rv->region()->set_position (where, current_music_divisor (where, ev_state));
1691 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1694 if (changed_tracks) {
1696 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1697 was selected in all of them, then removing it from a playlist will have removed all
1698 trace of it from _views (i.e. there were N regions selected, we removed 1,
1699 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1700 corresponding regionview, and _views is now empty).
1702 This could have invalidated any and all iterators into _views.
1704 The heuristic we use here is: if the region selection is empty, break out of the loop
1705 here. if the region selection is not empty, then restart the loop because we know that
1706 we must have removed at least the region(view) we've just been working on as well as any
1707 that we processed on previous iterations.
1709 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1710 we can just iterate.
1714 if (_views.empty()) {
1725 /* If we've created new regions either by copying or moving
1726 to a new track, we want to replace the old selection with the new ones
1729 if (new_views.size() > 0) {
1730 _editor->selection->set (new_views);
1733 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1737 /* write commands for the accumulated diffs for all our modified playlists */
1738 add_stateful_diff_commands_for_playlists (modified_playlists);
1739 /* applies to _brushing */
1740 _editor->commit_reversible_command ();
1742 /* We have futzed with the layering of canvas items on our streamviews.
1743 If any region changed layer, this will have resulted in the stream
1744 views being asked to set up their region views, and all will be well.
1745 If not, we might now have badly-ordered region views. Ask the StreamViews
1746 involved to sort themselves out, just in case.
1749 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1750 (*i)->view()->playlist_layered ((*i)->track ());
1754 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1755 * @param region Region to remove.
1756 * @param playlist playlist To remove from.
1757 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1758 * that clear_changes () is only called once per playlist.
1761 RegionMoveDrag::remove_region_from_playlist (
1762 boost::shared_ptr<Region> region,
1763 boost::shared_ptr<Playlist> playlist,
1764 PlaylistSet& modified_playlists
1767 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1770 playlist->clear_changes ();
1773 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1777 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1778 * clearing the playlist's diff history first if necessary.
1779 * @param region Region to insert.
1780 * @param dest_rtv Destination RouteTimeAxisView.
1781 * @param dest_layer Destination layer.
1782 * @param where Destination position.
1783 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1784 * that clear_changes () is only called once per playlist.
1785 * @return New RegionView, or 0 if no insert was performed.
1788 RegionMoveDrag::insert_region_into_playlist (
1789 boost::shared_ptr<Region> region,
1790 RouteTimeAxisView* dest_rtv,
1793 PlaylistSet& modified_playlists,
1794 const int32_t sub_num
1797 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1798 if (!dest_playlist) {
1802 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1803 _new_region_view = 0;
1804 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1806 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1807 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1809 dest_playlist->clear_changes ();
1811 dest_playlist->add_region (region, where, 1.0, false, sub_num);
1813 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1814 dest_playlist->set_layer (region, dest_layer);
1819 assert (_new_region_view);
1821 return _new_region_view;
1825 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1827 _new_region_view = rv;
1831 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1833 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1834 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1836 _editor->session()->add_command (c);
1845 RegionMoveDrag::aborted (bool movement_occurred)
1849 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1850 list<DraggingView>::const_iterator next = i;
1859 RegionMotionDrag::aborted (movement_occurred);
1864 RegionMotionDrag::aborted (bool)
1866 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1868 StreamView* sview = (*i)->view();
1871 if (sview->layer_display() == Expanded) {
1872 sview->set_layer_display (Stacked);
1877 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1878 RegionView* rv = i->view;
1879 TimeAxisView* tv = &(rv->get_time_axis_view ());
1880 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1882 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1883 rv->get_canvas_group()->set_y_position (0);
1885 rv->move (-_total_x_delta, 0);
1886 rv->set_height (rtv->view()->child_height ());
1890 /** @param b true to brush, otherwise false.
1891 * @param c true to make copies of the regions being moved, otherwise false.
1893 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1894 : RegionMotionDrag (e, i, p, v, b)
1896 , _new_region_view (0)
1898 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1901 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1902 if (rtv && rtv->is_track()) {
1903 speed = rtv->track()->speed ();
1906 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1910 RegionMoveDrag::setup_pointer_frame_offset ()
1912 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1915 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1916 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1918 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1920 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1921 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1923 _primary = v->view()->create_region_view (r, false, false);
1925 _primary->get_canvas_group()->show ();
1926 _primary->set_position (pos, 0);
1927 _views.push_back (DraggingView (_primary, this, v));
1929 _last_frame_position = pos;
1931 _item = _primary->get_canvas_group ();
1935 RegionInsertDrag::finished (GdkEvent * event, bool)
1937 int pos = _views.front().time_axis_view;
1938 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1940 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1942 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1943 _primary->get_canvas_group()->set_y_position (0);
1945 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1947 _editor->begin_reversible_command (Operations::insert_region);
1948 playlist->clear_changes ();
1949 playlist->add_region (_primary->region (), _last_frame_position);
1951 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1952 if (Config->get_edit_mode() == Ripple) {
1953 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1956 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1957 _editor->commit_reversible_command ();
1965 RegionInsertDrag::aborted (bool)
1972 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1973 : RegionMoveDrag (e, i, p, v, false, false)
1975 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1978 struct RegionSelectionByPosition {
1979 bool operator() (RegionView*a, RegionView* b) {
1980 return a->region()->position () < b->region()->position();
1985 RegionSpliceDrag::motion (GdkEvent* event, bool)
1987 /* Which trackview is this ? */
1989 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1990 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1992 /* The region motion is only processed if the pointer is over
1996 if (!tv || !tv->is_track()) {
1997 /* To make sure we hide the verbose canvas cursor when the mouse is
1998 not held over an audio track.
2000 _editor->verbose_cursor()->hide ();
2003 _editor->verbose_cursor()->show ();
2008 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2014 RegionSelection copy;
2015 _editor->selection->regions.by_position(copy);
2017 framepos_t const pf = adjusted_current_frame (event);
2019 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2021 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2027 boost::shared_ptr<Playlist> playlist;
2029 if ((playlist = atv->playlist()) == 0) {
2033 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2038 if (pf < (*i)->region()->last_frame() + 1) {
2042 if (pf > (*i)->region()->first_frame()) {
2048 playlist->shuffle ((*i)->region(), dir);
2053 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2055 RegionMoveDrag::finished (event, movement_occurred);
2059 RegionSpliceDrag::aborted (bool)
2069 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2072 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2074 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2075 RegionSelection to_ripple;
2076 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2077 if ((*i)->position() >= where) {
2078 to_ripple.push_back (rtv->view()->find_view(*i));
2082 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2083 if (!exclude.contains (*i)) {
2084 // the selection has already been added to _views
2086 if (drag_in_progress) {
2087 // do the same things that RegionMotionDrag::motion does when
2088 // first_move is true, for the region views that we're adding
2089 // to _views this time
2092 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2093 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2094 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2095 rvg->reparent (_editor->_drag_motion_group);
2097 // we only need to move in the y direction
2098 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2103 _views.push_back (DraggingView (*i, this, tav));
2109 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2112 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2113 // we added all the regions after the selection
2115 std::list<DraggingView>::iterator to_erase = i++;
2116 if (!_editor->selection->regions.contains (to_erase->view)) {
2117 // restore the non-selected regions to their original playlist & positions,
2118 // and then ripple them back by the length of the regions that were dragged away
2119 // do the same things as RegionMotionDrag::aborted
2121 RegionView *rv = to_erase->view;
2122 TimeAxisView* tv = &(rv->get_time_axis_view ());
2123 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2126 // plonk them back onto their own track
2127 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2128 rv->get_canvas_group()->set_y_position (0);
2132 // move the underlying region to match the view
2133 rv->region()->set_position (rv->region()->position() + amount);
2135 // restore the view to match the underlying region's original position
2136 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2139 rv->set_height (rtv->view()->child_height ());
2140 _views.erase (to_erase);
2146 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2148 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2150 return allow_moves_across_tracks;
2158 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2159 : RegionMoveDrag (e, i, p, v, false, false)
2161 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2162 // compute length of selection
2163 RegionSelection selected_regions = _editor->selection->regions;
2164 selection_length = selected_regions.end_frame() - selected_regions.start();
2166 // we'll only allow dragging to another track in ripple mode if all the regions
2167 // being dragged start off on the same track
2168 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2171 exclude = new RegionList;
2172 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2173 exclude->push_back((*i)->region());
2176 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2177 RegionSelection copy;
2178 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2180 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2181 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2183 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2184 // find ripple start point on each applicable playlist
2185 RegionView *first_selected_on_this_track = NULL;
2186 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2187 if ((*i)->region()->playlist() == (*pi)) {
2188 // region is on this playlist - it's the first, because they're sorted
2189 first_selected_on_this_track = *i;
2193 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2194 add_all_after_to_views (
2195 &first_selected_on_this_track->get_time_axis_view(),
2196 first_selected_on_this_track->region()->position(),
2197 selected_regions, false);
2200 if (allow_moves_across_tracks) {
2201 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2209 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2211 /* Which trackview is this ? */
2213 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2214 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2216 /* The region motion is only processed if the pointer is over
2220 if (!tv || !tv->is_track()) {
2221 /* To make sure we hide the verbose canvas cursor when the mouse is
2222 not held over an audiotrack.
2224 _editor->verbose_cursor()->hide ();
2228 framepos_t where = adjusted_current_frame (event);
2229 assert (where >= 0);
2231 double delta = compute_x_delta (event, &after);
2233 framecnt_t amount = _editor->pixel_to_sample (delta);
2235 if (allow_moves_across_tracks) {
2236 // all the originally selected regions were on the same track
2238 framecnt_t adjust = 0;
2239 if (prev_tav && tv != prev_tav) {
2240 // dragged onto a different track
2241 // remove the unselected regions from _views, restore them to their original positions
2242 // and add the regions after the drop point on the new playlist to _views instead.
2243 // undo the effect of rippling the previous playlist, and include the effect of removing
2244 // the dragged region(s) from this track
2246 remove_unselected_from_views (prev_amount, false);
2247 // ripple previous playlist according to the regions that have been removed onto the new playlist
2248 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2251 // move just the selected regions
2252 RegionMoveDrag::motion(event, first_move);
2254 // ensure that the ripple operation on the new playlist inserts selection_length time
2255 adjust = selection_length;
2256 // ripple the new current playlist
2257 tv->playlist()->ripple (where, amount+adjust, exclude);
2259 // add regions after point where drag entered this track to subsequent ripples
2260 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2263 // motion on same track
2264 RegionMoveDrag::motion(event, first_move);
2268 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2269 prev_position = where;
2271 // selection encompasses multiple tracks - just drag
2272 // cross-track drags are forbidden
2273 RegionMoveDrag::motion(event, first_move);
2276 if (!_x_constrained) {
2277 prev_amount += amount;
2280 _last_frame_position = after;
2284 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2286 if (!movement_occurred) {
2290 if (was_double_click() && !_views.empty()) {
2291 DraggingView dv = _views.front();
2292 dv.view->show_region_editor ();
2299 _editor->begin_reversible_command(_("Ripple drag"));
2301 // remove the regions being rippled from the dragging view, updating them to
2302 // their new positions
2303 remove_unselected_from_views (prev_amount, true);
2305 if (allow_moves_across_tracks) {
2307 // if regions were dragged across tracks, we've rippled any later
2308 // regions on the track the regions were dragged off, so we need
2309 // to add the original track to the undo record
2310 orig_tav->playlist()->clear_changes();
2311 vector<Command*> cmds;
2312 orig_tav->playlist()->rdiff (cmds);
2313 _editor->session()->add_commands (cmds);
2315 if (prev_tav && prev_tav != orig_tav) {
2316 prev_tav->playlist()->clear_changes();
2317 vector<Command*> cmds;
2318 prev_tav->playlist()->rdiff (cmds);
2319 _editor->session()->add_commands (cmds);
2322 // selection spanned multiple tracks - all will need adding to undo record
2324 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2325 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2327 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2328 (*pi)->clear_changes();
2329 vector<Command*> cmds;
2330 (*pi)->rdiff (cmds);
2331 _editor->session()->add_commands (cmds);
2335 // other modified playlists are added to undo by RegionMoveDrag::finished()
2336 RegionMoveDrag::finished (event, movement_occurred);
2337 _editor->commit_reversible_command();
2341 RegionRippleDrag::aborted (bool movement_occurred)
2343 RegionMoveDrag::aborted (movement_occurred);
2348 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2350 _view (dynamic_cast<MidiTimeAxisView*> (v))
2352 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2358 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2361 _editor->begin_reversible_command (_("create region"));
2362 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2363 _view->playlist()->freeze ();
2366 framepos_t const f = adjusted_current_frame (event);
2367 if (f < grab_frame()) {
2368 _region->set_initial_position (f);
2371 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2372 so that if this region is duplicated, its duplicate starts on
2373 a snap point rather than 1 frame after a snap point. Otherwise things get
2374 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2375 place snapped notes at the start of the region.
2378 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2379 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2385 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2387 if (!movement_occurred) {
2388 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2390 _view->playlist()->thaw ();
2391 _editor->commit_reversible_command();
2396 RegionCreateDrag::aborted (bool)
2399 _view->playlist()->thaw ();
2405 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2410 , _was_selected (false)
2413 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2417 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2419 Gdk::Cursor* cursor;
2420 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2422 float x_fraction = cnote->mouse_x_fraction ();
2424 if (x_fraction > 0.0 && x_fraction < 0.25) {
2425 cursor = _editor->cursors()->left_side_trim;
2428 cursor = _editor->cursors()->right_side_trim;
2432 Drag::start_grab (event, cursor);
2434 region = &cnote->region_view();
2437 temp = region->snap_to_pixel (cnote->x0 (), true);
2438 _snap_delta = temp - cnote->x0 ();
2442 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2447 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2448 if (ms.size() > 1) {
2449 /* has to be relative, may make no sense otherwise */
2453 if (!(_was_selected = cnote->selected())) {
2455 /* tertiary-click means extend selection - we'll do that on button release,
2456 so don't add it here, because otherwise we make it hard to figure
2457 out the "extend-to" range.
2460 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2463 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2466 region->note_selected (cnote, true);
2468 _editor->get_selection().clear_points();
2469 region->unique_select (cnote);
2476 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2478 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2480 _editor->begin_reversible_command (_("resize notes"));
2482 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2483 MidiRegionSelection::iterator next;
2486 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2488 mrv->begin_resizing (at_front);
2494 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2495 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2497 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2501 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2503 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2504 if (_editor->snap_mode () != SnapOff) {
2508 if (_editor->snap_mode () == SnapOff) {
2510 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2511 if (apply_snap_delta) {
2517 if (apply_snap_delta) {
2521 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2527 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2529 if (!movement_occurred) {
2530 /* no motion - select note */
2531 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2532 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2533 _editor->current_mouse_mode() == Editing::MouseDraw) {
2535 bool changed = false;
2537 if (_was_selected) {
2538 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2540 region->note_deselected (cnote);
2543 _editor->get_selection().clear_points();
2544 region->unique_select (cnote);
2548 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2549 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2551 if (!extend && !add && region->selection_size() > 1) {
2552 _editor->get_selection().clear_points();
2553 region->unique_select (cnote);
2555 } else if (extend) {
2556 region->note_selected (cnote, true, true);
2559 /* it was added during button press */
2565 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2566 _editor->commit_reversible_selection_op();
2573 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2574 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2575 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2577 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2580 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2582 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2583 if (_editor->snap_mode () != SnapOff) {
2587 if (_editor->snap_mode () == SnapOff) {
2589 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2590 if (apply_snap_delta) {
2596 if (apply_snap_delta) {
2600 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2604 _editor->commit_reversible_command ();
2608 NoteResizeDrag::aborted (bool)
2610 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2611 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2612 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2614 mrv->abort_resizing ();
2619 AVDraggingView::AVDraggingView (RegionView* v)
2622 initial_position = v->region()->position ();
2625 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2628 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2631 TrackViewList empty;
2633 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2634 std::list<RegionView*> views = rs.by_layer();
2637 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2638 RegionView* rv = (*i);
2639 if (!rv->region()->video_locked()) {
2642 if (rv->region()->locked()) {
2645 _views.push_back (AVDraggingView (rv));
2650 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2652 Drag::start_grab (event);
2653 if (_editor->session() == 0) {
2657 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2663 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2667 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2668 _max_backwards_drag = (
2669 ARDOUR_UI::instance()->video_timeline->get_duration()
2670 + ARDOUR_UI::instance()->video_timeline->get_offset()
2671 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2674 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2675 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2676 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2679 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2682 Timecode::Time timecode;
2683 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2684 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);
2685 show_verbose_cursor_text (buf);
2689 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2691 if (_editor->session() == 0) {
2694 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2698 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2702 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2703 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2705 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2706 dt = - _max_backwards_drag;
2709 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2710 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2712 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2713 RegionView* rv = i->view;
2714 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2717 rv->region()->clear_changes ();
2718 rv->region()->suspend_property_changes();
2720 rv->region()->set_position(i->initial_position + dt);
2721 rv->region_changed(ARDOUR::Properties::position);
2724 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2725 Timecode::Time timecode;
2726 Timecode::Time timediff;
2728 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2729 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2730 snprintf (buf, sizeof (buf),
2731 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2732 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2733 , _("Video Start:"),
2734 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2736 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2738 show_verbose_cursor_text (buf);
2742 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2744 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2751 if (!movement_occurred || ! _editor->session()) {
2755 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2757 _editor->begin_reversible_command (_("Move Video"));
2759 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2760 ARDOUR_UI::instance()->video_timeline->save_undo();
2761 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2762 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2764 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2765 i->view->drag_end();
2766 i->view->region()->resume_property_changes ();
2768 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2771 _editor->session()->maybe_update_session_range(
2772 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2773 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2777 _editor->commit_reversible_command ();
2781 VideoTimeLineDrag::aborted (bool)
2783 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2786 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2787 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2789 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2790 i->view->region()->resume_property_changes ();
2791 i->view->region()->set_position(i->initial_position);
2795 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2796 : RegionDrag (e, i, p, v)
2797 , _operation (StartTrim)
2798 , _preserve_fade_anchor (preserve_fade_anchor)
2799 , _jump_position_when_done (false)
2801 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2805 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2808 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2809 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2811 if (tv && tv->is_track()) {
2812 speed = tv->track()->speed();
2815 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2816 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2817 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2819 framepos_t const pf = adjusted_current_frame (event);
2820 setup_snap_delta (region_start);
2822 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2823 /* Move the contents of the region around without changing the region bounds */
2824 _operation = ContentsTrim;
2825 Drag::start_grab (event, _editor->cursors()->trimmer);
2827 /* These will get overridden for a point trim.*/
2828 if (pf < (region_start + region_length/2)) {
2829 /* closer to front */
2830 _operation = StartTrim;
2831 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2832 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2834 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2838 _operation = EndTrim;
2839 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2840 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2842 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2846 /* jump trim disabled for now
2847 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2848 _jump_position_when_done = true;
2852 switch (_operation) {
2854 show_verbose_cursor_time (region_start);
2857 show_verbose_cursor_duration (region_start, region_end);
2860 show_verbose_cursor_time (pf);
2864 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2865 i->view->region()->suspend_property_changes ();
2870 TrimDrag::motion (GdkEvent* event, bool first_move)
2872 RegionView* rv = _primary;
2875 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2876 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2877 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2878 frameoffset_t frame_delta = 0;
2880 if (tv && tv->is_track()) {
2881 speed = tv->track()->speed();
2883 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2884 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2890 switch (_operation) {
2892 trim_type = "Region start trim";
2895 trim_type = "Region end trim";
2898 trim_type = "Region content trim";
2905 _editor->begin_reversible_command (trim_type);
2907 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2908 RegionView* rv = i->view;
2909 rv->region()->playlist()->clear_owned_changes ();
2911 if (_operation == StartTrim) {
2912 rv->trim_front_starting ();
2915 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2918 arv->temporarily_hide_envelope ();
2922 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2923 insert_result = _editor->motion_frozen_playlists.insert (pl);
2925 if (insert_result.second) {
2931 bool non_overlap_trim = false;
2933 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2934 non_overlap_trim = true;
2937 /* contstrain trim to fade length */
2938 if (_preserve_fade_anchor) {
2939 switch (_operation) {
2941 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2942 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2944 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2945 if (ar->locked()) continue;
2946 framecnt_t len = ar->fade_in()->back()->when;
2947 if (len < dt) dt = min(dt, len);
2951 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2952 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2954 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2955 if (ar->locked()) continue;
2956 framecnt_t len = ar->fade_out()->back()->when;
2957 if (len < -dt) dt = max(dt, -len);
2965 int32_t divisions = current_music_divisor (adj_frame, event->button.state);
2967 switch (_operation) {
2969 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2970 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2973 if (changed && _preserve_fade_anchor) {
2974 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2976 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2977 framecnt_t len = ar->fade_in()->back()->when;
2978 framecnt_t diff = ar->first_frame() - i->initial_position;
2979 framepos_t new_length = len - diff;
2980 i->anchored_fade_length = min (ar->length(), new_length);
2981 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2982 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2989 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2990 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, divisions);
2991 if (changed && _preserve_fade_anchor) {
2992 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2994 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2995 framecnt_t len = ar->fade_out()->back()->when;
2996 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2997 framepos_t new_length = len + diff;
2998 i->anchored_fade_length = min (ar->length(), new_length);
2999 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3000 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3008 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3010 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3011 i->view->move_contents (frame_delta);
3017 switch (_operation) {
3019 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3022 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3025 // show_verbose_cursor_time (frame_delta);
3031 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3033 if (movement_occurred) {
3034 motion (event, false);
3036 if (_operation == StartTrim) {
3037 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3039 /* This must happen before the region's StatefulDiffCommand is created, as it may
3040 `correct' (ahem) the region's _start from being negative to being zero. It
3041 needs to be zero in the undo record.
3043 i->view->trim_front_ending ();
3045 if (_preserve_fade_anchor) {
3046 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3048 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3049 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3050 ar->set_fade_in_length(i->anchored_fade_length);
3051 ar->set_fade_in_active(true);
3054 if (_jump_position_when_done) {
3055 i->view->region()->set_position (i->initial_position);
3058 } else if (_operation == EndTrim) {
3059 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3060 if (_preserve_fade_anchor) {
3061 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3063 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3064 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3065 ar->set_fade_out_length(i->anchored_fade_length);
3066 ar->set_fade_out_active(true);
3069 if (_jump_position_when_done) {
3070 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3075 if (!_views.empty()) {
3076 if (_operation == StartTrim) {
3077 _editor->maybe_locate_with_edit_preroll(
3078 _views.begin()->view->region()->position());
3080 if (_operation == EndTrim) {
3081 _editor->maybe_locate_with_edit_preroll(
3082 _views.begin()->view->region()->position() +
3083 _views.begin()->view->region()->length());
3087 if (!_editor->selection->selected (_primary)) {
3088 _primary->thaw_after_trim ();
3091 set<boost::shared_ptr<Playlist> > diffed_playlists;
3093 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3094 i->view->thaw_after_trim ();
3095 i->view->enable_display (true);
3097 /* Trimming one region may affect others on the playlist, so we need
3098 to get undo Commands from the whole playlist rather than just the
3099 region. Use diffed_playlists to make sure we don't diff a given
3100 playlist more than once.
3102 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3103 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3104 vector<Command*> cmds;
3106 _editor->session()->add_commands (cmds);
3107 diffed_playlists.insert (p);
3112 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3116 _editor->motion_frozen_playlists.clear ();
3117 _editor->commit_reversible_command();
3120 /* no mouse movement */
3121 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3122 _editor->point_trim (event, adjusted_current_frame (event));
3126 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3127 i->view->region()->resume_property_changes ();
3132 TrimDrag::aborted (bool movement_occurred)
3134 /* Our motion method is changing model state, so use the Undo system
3135 to cancel. Perhaps not ideal, as this will leave an Undo point
3136 behind which may be slightly odd from the user's point of view.
3140 finished (&ev, true);
3142 if (movement_occurred) {
3143 _editor->session()->undo (1);
3146 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3147 i->view->region()->resume_property_changes ();
3152 TrimDrag::setup_pointer_frame_offset ()
3154 list<DraggingView>::iterator i = _views.begin ();
3155 while (i != _views.end() && i->view != _primary) {
3159 if (i == _views.end()) {
3163 switch (_operation) {
3165 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3168 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3175 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3178 , _old_snap_type (e->snap_type())
3179 , _old_snap_mode (e->snap_mode())
3182 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3183 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3185 _real_section = &_marker->meter();
3190 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3192 Drag::start_grab (event, cursor);
3193 show_verbose_cursor_time (adjusted_current_frame(event));
3197 MeterMarkerDrag::setup_pointer_frame_offset ()
3199 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3203 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3206 // create a dummy marker to catch events, then hide it.
3209 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3211 _marker = new MeterMarker (
3213 *_editor->meter_group,
3214 UIConfiguration::instance().color ("meter marker"),
3216 *new MeterSection (_marker->meter())
3219 /* use the new marker for the grab */
3220 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3223 TempoMap& map (_editor->session()->tempo_map());
3224 /* get current state */
3225 before_state = &map.get_state();
3228 _editor->begin_reversible_command (_("move meter mark"));
3230 _editor->begin_reversible_command (_("copy meter mark"));
3232 Timecode::BBT_Time bbt = _real_section->bbt();
3234 /* we can't add a meter where one currently exists */
3235 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3240 const double beat = map.beat_at_bbt (bbt);
3241 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3242 , beat, bbt, _real_section->position_lock_style());
3243 if (!_real_section) {
3249 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3250 if (_real_section->position_lock_style() != AudioTime) {
3251 _editor->set_snap_to (SnapToBar);
3252 _editor->set_snap_mode (SnapNormal);
3256 framepos_t pf = adjusted_current_frame (event);
3258 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3259 /* never snap to music for audio locked */
3260 pf = adjusted_current_frame (event, false);
3263 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3265 /* fake marker meeds to stay under the mouse, unlike the real one. */
3266 _marker->set_position (adjusted_current_frame (event, false));
3268 show_verbose_cursor_time (_real_section->frame());
3272 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3274 if (!movement_occurred) {
3275 if (was_double_click()) {
3276 _editor->edit_meter_marker (*_marker);
3281 /* reinstate old snap setting */
3282 _editor->set_snap_to (_old_snap_type);
3283 _editor->set_snap_mode (_old_snap_mode);
3285 TempoMap& map (_editor->session()->tempo_map());
3287 XMLNode &after = map.get_state();
3288 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3289 _editor->commit_reversible_command ();
3291 // delete the dummy marker we used for visual representation while moving.
3292 // a new visual marker will show up automatically.
3297 MeterMarkerDrag::aborted (bool moved)
3299 _marker->set_position (_marker->meter().frame ());
3301 /* reinstate old snap setting */
3302 _editor->set_snap_to (_old_snap_type);
3303 _editor->set_snap_mode (_old_snap_mode);
3305 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3306 // delete the dummy marker we used for visual representation while moving.
3307 // a new visual marker will show up automatically.
3312 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3318 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3320 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3321 _real_section = &_marker->tempo();
3322 _movable = !_real_section->initial();
3323 _grab_bpm = _real_section->note_types_per_minute();
3328 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3330 Drag::start_grab (event, cursor);
3331 if (!_real_section->active()) {
3332 show_verbose_cursor_text (_("inactive"));
3334 show_verbose_cursor_time (adjusted_current_frame (event));
3339 TempoMarkerDrag::setup_pointer_frame_offset ()
3341 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3345 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3347 if (!_real_section->active()) {
3353 // mvc drag - create a dummy marker to catch events, hide it.
3356 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3358 TempoSection section (_marker->tempo());
3360 _marker = new TempoMarker (
3362 *_editor->tempo_group,
3363 UIConfiguration::instance().color ("tempo marker"),
3365 *new TempoSection (_marker->tempo())
3368 /* use the new marker for the grab */
3369 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3372 TempoMap& map (_editor->session()->tempo_map());
3373 /* get current state */
3374 before_state = &map.get_state();
3377 _editor->begin_reversible_command (_("move tempo mark"));
3380 const Tempo tempo (_marker->tempo());
3381 const framepos_t frame = adjusted_current_frame (event) + 1;
3382 const TempoSection::Type type = _real_section->type();
3384 _editor->begin_reversible_command (_("copy tempo mark"));
3386 if (_real_section->position_lock_style() == MusicTime) {
3387 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3388 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3390 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3393 if (!_real_section) {
3401 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3402 /* use vertical movement to alter tempo .. should be log */
3403 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3405 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3407 show_verbose_cursor_text (strs.str());
3409 } else if (_movable && !_real_section->locked_to_meter()) {
3412 if (_editor->snap_musical()) {
3413 /* we can't snap to a grid that we are about to move.
3414 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3416 pf = adjusted_current_frame (event, false);
3418 pf = adjusted_current_frame (event);
3421 TempoMap& map (_editor->session()->tempo_map());
3423 /* snap to beat is 1, snap to bar is -1 (sorry) */
3424 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3426 map.gui_move_tempo (_real_section, pf, sub_num);
3428 show_verbose_cursor_time (_real_section->frame());
3430 _marker->set_position (adjusted_current_frame (event, false));
3434 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3436 if (!_real_section->active()) {
3439 if (!movement_occurred) {
3440 if (was_double_click()) {
3441 _editor->edit_tempo_marker (*_marker);
3446 TempoMap& map (_editor->session()->tempo_map());
3448 XMLNode &after = map.get_state();
3449 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3450 _editor->commit_reversible_command ();
3452 // delete the dummy marker we used for visual representation while moving.
3453 // a new visual marker will show up automatically.
3458 TempoMarkerDrag::aborted (bool moved)
3460 _marker->set_position (_marker->tempo().frame());
3462 TempoMap& map (_editor->session()->tempo_map());
3463 map.set_state (*before_state, Stateful::current_state_version);
3464 // delete the dummy (hidden) marker we used for events while moving.
3469 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3475 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3480 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3482 Drag::start_grab (event, cursor);
3483 TempoMap& map (_editor->session()->tempo_map());
3484 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3487 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3488 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3489 show_verbose_cursor_text (sstr.str());
3490 finished (event, false);
3494 BBTRulerDrag::setup_pointer_frame_offset ()
3496 TempoMap& map (_editor->session()->tempo_map());
3497 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3498 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3501 if (divisions > 0) {
3502 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3504 /* while it makes some sense for the user to determine the division to 'grab',
3505 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3506 and the result over steep tempo curves. Use sixteenths.
3508 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3511 _grab_qn = map.quarter_note_at_beat (beat);
3513 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3518 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3520 TempoMap& map (_editor->session()->tempo_map());
3523 /* get current state */
3524 before_state = &map.get_state();
3525 _editor->begin_reversible_command (_("dilate tempo"));
3530 if (_editor->snap_musical()) {
3531 pf = adjusted_current_frame (event, false);
3533 pf = adjusted_current_frame (event);
3536 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3537 /* adjust previous tempo to match pointer frame */
3538 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3541 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3542 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3543 show_verbose_cursor_text (sstr.str());
3547 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3549 if (!movement_occurred) {
3553 TempoMap& map (_editor->session()->tempo_map());
3555 XMLNode &after = map.get_state();
3556 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3557 _editor->commit_reversible_command ();
3561 BBTRulerDrag::aborted (bool moved)
3564 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3569 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3570 : Drag (e, &c.track_canvas_item(), false)
3575 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3578 /** Do all the things we do when dragging the playhead to make it look as though
3579 * we have located, without actually doing the locate (because that would cause
3580 * the diskstream buffers to be refilled, which is too slow).
3583 CursorDrag::fake_locate (framepos_t t)
3585 if (_editor->session () == 0) {
3589 _editor->playhead_cursor->set_position (t);
3591 Session* s = _editor->session ();
3592 if (s->timecode_transmission_suspended ()) {
3593 framepos_t const f = _editor->playhead_cursor->current_frame ();
3594 /* This is asynchronous so it will be sent "now"
3596 s->send_mmc_locate (f);
3597 /* These are synchronous and will be sent during the next
3600 s->queue_full_time_code ();
3601 s->queue_song_position_pointer ();
3604 show_verbose_cursor_time (t);
3605 _editor->UpdateAllTransportClocks (t);
3609 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3611 Drag::start_grab (event, c);
3612 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3614 _grab_zoom = _editor->samples_per_pixel;
3616 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3618 _editor->snap_to_with_modifier (where, event);
3620 _editor->_dragging_playhead = true;
3622 Session* s = _editor->session ();
3624 /* grab the track canvas item as well */
3626 _cursor.track_canvas_item().grab();
3629 if (_was_rolling && _stop) {
3633 if (s->is_auditioning()) {
3634 s->cancel_audition ();
3638 if (AudioEngine::instance()->connected()) {
3640 /* do this only if we're the engine is connected
3641 * because otherwise this request will never be
3642 * serviced and we'll busy wait forever. likewise,
3643 * notice if we are disconnected while waiting for the
3644 * request to be serviced.
3647 s->request_suspend_timecode_transmission ();
3648 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3649 /* twiddle our thumbs */
3654 fake_locate (where - snap_delta (event->button.state));
3658 CursorDrag::motion (GdkEvent* event, bool)
3660 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3661 _editor->snap_to_with_modifier (where, event);
3662 if (where != last_pointer_frame()) {
3663 fake_locate (where - snap_delta (event->button.state));
3668 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3670 _editor->_dragging_playhead = false;
3672 _cursor.track_canvas_item().ungrab();
3674 if (!movement_occurred && _stop) {
3678 motion (event, false);
3680 Session* s = _editor->session ();
3682 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3683 _editor->_pending_locate_request = true;
3684 s->request_resume_timecode_transmission ();
3689 CursorDrag::aborted (bool)
3691 _cursor.track_canvas_item().ungrab();
3693 if (_editor->_dragging_playhead) {
3694 _editor->session()->request_resume_timecode_transmission ();
3695 _editor->_dragging_playhead = false;
3698 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3701 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3702 : RegionDrag (e, i, p, v)
3704 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3708 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3710 Drag::start_grab (event, cursor);
3712 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3713 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3714 setup_snap_delta (r->position ());
3716 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3720 FadeInDrag::setup_pointer_frame_offset ()
3722 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3723 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3724 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3728 FadeInDrag::motion (GdkEvent* event, bool)
3730 framecnt_t fade_length;
3732 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3733 _editor->snap_to_with_modifier (pos, event);
3734 pos -= snap_delta (event->button.state);
3736 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3738 if (pos < (region->position() + 64)) {
3739 fade_length = 64; // this should be a minimum defined somewhere
3740 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3741 fade_length = region->length() - region->fade_out()->back()->when - 1;
3743 fade_length = pos - region->position();
3746 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3748 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3754 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3757 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3761 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3763 if (!movement_occurred) {
3767 framecnt_t fade_length;
3768 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3769 _editor->snap_to_with_modifier (pos, event);
3770 pos -= snap_delta (event->button.state);
3772 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3774 if (pos < (region->position() + 64)) {
3775 fade_length = 64; // this should be a minimum defined somewhere
3776 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3777 fade_length = region->length() - region->fade_out()->back()->when - 1;
3779 fade_length = pos - region->position();
3782 bool in_command = false;
3784 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3786 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3792 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3793 XMLNode &before = alist->get_state();
3795 tmp->audio_region()->set_fade_in_length (fade_length);
3796 tmp->audio_region()->set_fade_in_active (true);
3799 _editor->begin_reversible_command (_("change fade in length"));
3802 XMLNode &after = alist->get_state();
3803 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3807 _editor->commit_reversible_command ();
3812 FadeInDrag::aborted (bool)
3814 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3815 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3821 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3825 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3826 : RegionDrag (e, i, p, v)
3828 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3832 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3834 Drag::start_grab (event, cursor);
3836 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3837 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3838 setup_snap_delta (r->last_frame ());
3840 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3844 FadeOutDrag::setup_pointer_frame_offset ()
3846 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3847 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3848 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3852 FadeOutDrag::motion (GdkEvent* event, bool)
3854 framecnt_t fade_length;
3856 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3857 _editor->snap_to_with_modifier (pos, event);
3858 pos -= snap_delta (event->button.state);
3860 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3862 if (pos > (region->last_frame() - 64)) {
3863 fade_length = 64; // this should really be a minimum fade defined somewhere
3864 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3865 fade_length = region->length() - region->fade_in()->back()->when - 1;
3867 fade_length = region->last_frame() - pos;
3870 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3872 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3878 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3881 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3885 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3887 if (!movement_occurred) {
3891 framecnt_t fade_length;
3893 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3894 _editor->snap_to_with_modifier (pos, event);
3895 pos -= snap_delta (event->button.state);
3897 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3899 if (pos > (region->last_frame() - 64)) {
3900 fade_length = 64; // this should really be a minimum fade defined somewhere
3901 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3902 fade_length = region->length() - region->fade_in()->back()->when - 1;
3904 fade_length = region->last_frame() - pos;
3907 bool in_command = false;
3909 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3911 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3917 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3918 XMLNode &before = alist->get_state();
3920 tmp->audio_region()->set_fade_out_length (fade_length);
3921 tmp->audio_region()->set_fade_out_active (true);
3924 _editor->begin_reversible_command (_("change fade out length"));
3927 XMLNode &after = alist->get_state();
3928 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3932 _editor->commit_reversible_command ();
3937 FadeOutDrag::aborted (bool)
3939 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3940 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3946 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3950 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3952 , _selection_changed (false)
3954 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3955 Gtk::Window* toplevel = _editor->current_toplevel();
3956 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3960 _points.push_back (ArdourCanvas::Duple (0, 0));
3962 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3965 MarkerDrag::~MarkerDrag ()
3967 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3972 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3974 location = new Location (*l);
3975 markers.push_back (m);
3980 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3982 Drag::start_grab (event, cursor);
3986 Location *location = _editor->find_location_from_marker (_marker, is_start);
3987 _editor->_dragging_edit_point = true;
3989 update_item (location);
3991 // _drag_line->show();
3992 // _line->raise_to_top();
3995 show_verbose_cursor_time (location->start());
3997 show_verbose_cursor_time (location->end());
3999 setup_snap_delta (is_start ? location->start() : location->end());
4001 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4004 case Selection::Toggle:
4005 /* we toggle on the button release */
4007 case Selection::Set:
4008 if (!_editor->selection->selected (_marker)) {
4009 _editor->selection->set (_marker);
4010 _selection_changed = true;
4013 case Selection::Extend:
4015 Locations::LocationList ll;
4016 list<ArdourMarker*> to_add;
4018 _editor->selection->markers.range (s, e);
4019 s = min (_marker->position(), s);
4020 e = max (_marker->position(), e);
4023 if (e < max_framepos) {
4026 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4027 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4028 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4031 to_add.push_back (lm->start);
4034 to_add.push_back (lm->end);
4038 if (!to_add.empty()) {
4039 _editor->selection->add (to_add);
4040 _selection_changed = true;
4044 case Selection::Add:
4045 _editor->selection->add (_marker);
4046 _selection_changed = true;
4051 /* Set up copies for us to manipulate during the drag
4054 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4056 Location* l = _editor->find_location_from_marker (*i, is_start);
4063 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4065 /* range: check that the other end of the range isn't
4068 CopiedLocationInfo::iterator x;
4069 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4070 if (*(*x).location == *l) {
4074 if (x == _copied_locations.end()) {
4075 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4077 (*x).markers.push_back (*i);
4078 (*x).move_both = true;
4086 MarkerDrag::setup_pointer_frame_offset ()
4089 Location *location = _editor->find_location_from_marker (_marker, is_start);
4090 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4094 MarkerDrag::motion (GdkEvent* event, bool)
4096 framecnt_t f_delta = 0;
4098 bool move_both = false;
4099 Location *real_location;
4100 Location *copy_location = 0;
4101 framecnt_t const sd = snap_delta (event->button.state);
4103 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4104 framepos_t next = newframe;
4106 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4110 CopiedLocationInfo::iterator x;
4112 /* find the marker we're dragging, and compute the delta */
4114 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4116 copy_location = (*x).location;
4118 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4120 /* this marker is represented by this
4121 * CopiedLocationMarkerInfo
4124 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4129 if (real_location->is_mark()) {
4130 f_delta = newframe - copy_location->start();
4134 switch (_marker->type()) {
4135 case ArdourMarker::SessionStart:
4136 case ArdourMarker::RangeStart:
4137 case ArdourMarker::LoopStart:
4138 case ArdourMarker::PunchIn:
4139 f_delta = newframe - copy_location->start();
4142 case ArdourMarker::SessionEnd:
4143 case ArdourMarker::RangeEnd:
4144 case ArdourMarker::LoopEnd:
4145 case ArdourMarker::PunchOut:
4146 f_delta = newframe - copy_location->end();
4149 /* what kind of marker is this ? */
4158 if (x == _copied_locations.end()) {
4159 /* hmm, impossible - we didn't find the dragged marker */
4163 /* now move them all */
4165 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4167 copy_location = x->location;
4169 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4173 if (real_location->locked()) {
4177 if (copy_location->is_mark()) {
4181 copy_location->set_start (copy_location->start() + f_delta);
4185 framepos_t new_start = copy_location->start() + f_delta;
4186 framepos_t new_end = copy_location->end() + f_delta;
4188 if (is_start) { // start-of-range marker
4190 if (move_both || (*x).move_both) {
4191 copy_location->set_start (new_start);
4192 copy_location->set_end (new_end);
4193 } else if (new_start < copy_location->end()) {
4194 copy_location->set_start (new_start);
4195 } else if (newframe > 0) {
4196 //_editor->snap_to (next, RoundUpAlways, true);
4197 copy_location->set_end (next);
4198 copy_location->set_start (newframe);
4201 } else { // end marker
4203 if (move_both || (*x).move_both) {
4204 copy_location->set_end (new_end);
4205 copy_location->set_start (new_start);
4206 } else if (new_end > copy_location->start()) {
4207 copy_location->set_end (new_end);
4208 } else if (newframe > 0) {
4209 //_editor->snap_to (next, RoundDownAlways, true);
4210 copy_location->set_start (next);
4211 copy_location->set_end (newframe);
4216 update_item (copy_location);
4218 /* now lookup the actual GUI items used to display this
4219 * location and move them to wherever the copy of the location
4220 * is now. This means that the logic in ARDOUR::Location is
4221 * still enforced, even though we are not (yet) modifying
4222 * the real Location itself.
4225 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4228 lm->set_position (copy_location->start(), copy_location->end());
4233 assert (!_copied_locations.empty());
4235 show_verbose_cursor_time (newframe);
4239 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4241 if (!movement_occurred) {
4243 if (was_double_click()) {
4244 _editor->rename_marker (_marker);
4248 /* just a click, do nothing but finish
4249 off the selection process
4252 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4254 case Selection::Set:
4255 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4256 _editor->selection->set (_marker);
4257 _selection_changed = true;
4261 case Selection::Toggle:
4262 /* we toggle on the button release, click only */
4263 _editor->selection->toggle (_marker);
4264 _selection_changed = true;
4268 case Selection::Extend:
4269 case Selection::Add:
4273 if (_selection_changed) {
4274 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4275 _editor->commit_reversible_selection_op();
4281 _editor->_dragging_edit_point = false;
4283 XMLNode &before = _editor->session()->locations()->get_state();
4284 bool in_command = false;
4286 MarkerSelection::iterator i;
4287 CopiedLocationInfo::iterator x;
4290 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4291 x != _copied_locations.end() && i != _editor->selection->markers.end();
4294 Location * location = _editor->find_location_from_marker (*i, is_start);
4298 if (location->locked()) {
4302 _editor->begin_reversible_command ( _("move marker") );
4305 if (location->is_mark()) {
4306 location->set_start (((*x).location)->start());
4308 location->set (((*x).location)->start(), ((*x).location)->end());
4311 if (location->is_session_range()) {
4312 _editor->session()->set_end_is_free (false);
4318 XMLNode &after = _editor->session()->locations()->get_state();
4319 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4320 _editor->commit_reversible_command ();
4325 MarkerDrag::aborted (bool movement_occurred)
4327 if (!movement_occurred) {
4331 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4333 /* move all markers to their original location */
4336 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4339 Location * location = _editor->find_location_from_marker (*m, is_start);
4342 (*m)->set_position (is_start ? location->start() : location->end());
4349 MarkerDrag::update_item (Location*)
4354 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4356 , _fixed_grab_x (0.0)
4357 , _fixed_grab_y (0.0)
4358 , _cumulative_x_drag (0.0)
4359 , _cumulative_y_drag (0.0)
4363 if (_zero_gain_fraction < 0.0) {
4364 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4367 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4369 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4375 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4377 Drag::start_grab (event, _editor->cursors()->fader);
4379 // start the grab at the center of the control point so
4380 // the point doesn't 'jump' to the mouse after the first drag
4381 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4382 _fixed_grab_y = _point->get_y();
4384 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4385 setup_snap_delta (pos);
4387 float const fraction = 1 - (_point->get_y() / _point->line().height());
4388 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4390 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4392 if (!_point->can_slide ()) {
4393 _x_constrained = true;
4398 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4400 double dx = _drags->current_pointer_x() - last_pointer_x();
4401 double dy = current_pointer_y() - last_pointer_y();
4402 bool need_snap = true;
4404 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4410 /* coordinate in pixels relative to the start of the region (for region-based automation)
4411 or track (for track-based automation) */
4412 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4413 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4415 // calculate zero crossing point. back off by .01 to stay on the
4416 // positive side of zero
4417 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4419 if (_x_constrained) {
4422 if (_y_constrained) {
4426 _cumulative_x_drag = cx - _fixed_grab_x;
4427 _cumulative_y_drag = cy - _fixed_grab_y;
4431 cy = min ((double) _point->line().height(), cy);
4433 // make sure we hit zero when passing through
4434 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4438 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4439 if (!_x_constrained && need_snap) {
4440 _editor->snap_to_with_modifier (cx_frames, event);
4443 cx_frames -= snap_delta (event->button.state);
4444 cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4446 float const fraction = 1.0 - (cy / _point->line().height());
4449 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4450 _editor->begin_reversible_command (_("automation event move"));
4451 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4453 pair<double, float> result;
4454 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4456 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4460 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4462 if (!movement_occurred) {
4465 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4466 _editor->reset_point_selection ();
4470 _point->line().end_drag (_pushing, _final_index);
4471 _editor->commit_reversible_command ();
4476 ControlPointDrag::aborted (bool)
4478 _point->line().reset ();
4482 ControlPointDrag::active (Editing::MouseMode m)
4484 if (m == Editing::MouseDraw) {
4485 /* always active in mouse draw */
4489 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4490 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4493 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4496 , _fixed_grab_x (0.0)
4497 , _fixed_grab_y (0.0)
4498 , _cumulative_y_drag (0)
4502 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4506 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4508 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4511 _item = &_line->grab_item ();
4513 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4514 origin, and ditto for y.
4517 double mx = event->button.x;
4518 double my = event->button.y;
4520 _line->grab_item().canvas_to_item (mx, my);
4522 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4524 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4525 /* no adjacent points */
4529 Drag::start_grab (event, _editor->cursors()->fader);
4531 /* store grab start in item frame */
4532 double const bx = _line->nth (_before)->get_x();
4533 double const ax = _line->nth (_after)->get_x();
4534 double const click_ratio = (ax - mx) / (ax - bx);
4536 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4541 double fraction = 1.0 - (cy / _line->height());
4543 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4547 LineDrag::motion (GdkEvent* event, bool first_move)
4549 double dy = current_pointer_y() - last_pointer_y();
4551 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4555 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4557 _cumulative_y_drag = cy - _fixed_grab_y;
4560 cy = min ((double) _line->height(), cy);
4562 double const fraction = 1.0 - (cy / _line->height());
4566 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4568 _editor->begin_reversible_command (_("automation range move"));
4569 _line->start_drag_line (_before, _after, initial_fraction);
4572 /* we are ignoring x position for this drag, so we can just pass in anything */
4573 pair<double, float> result;
4575 result = _line->drag_motion (0, fraction, true, false, ignored);
4576 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4580 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4582 if (movement_occurred) {
4583 motion (event, false);
4584 _line->end_drag (false, 0);
4585 _editor->commit_reversible_command ();
4587 /* add a new control point on the line */
4589 AutomationTimeAxisView* atv;
4591 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4592 framepos_t where = grab_frame ();
4595 double cy = _fixed_grab_y;
4597 _line->grab_item().item_to_canvas (cx, cy);
4599 atv->add_automation_event (event, where, cy, false);
4600 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4601 AudioRegionView* arv;
4603 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4604 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4611 LineDrag::aborted (bool)
4616 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4620 _region_view_grab_x (0.0),
4621 _cumulative_x_drag (0),
4625 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4629 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4631 Drag::start_grab (event);
4633 _line = reinterpret_cast<Line*> (_item);
4636 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4638 double cx = event->button.x;
4639 double cy = event->button.y;
4641 _item->parent()->canvas_to_item (cx, cy);
4643 /* store grab start in parent frame */
4644 _region_view_grab_x = cx;
4646 _before = *(float*) _item->get_data ("position");
4648 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4650 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4654 FeatureLineDrag::motion (GdkEvent*, bool)
4656 double dx = _drags->current_pointer_x() - last_pointer_x();
4658 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4660 _cumulative_x_drag += dx;
4662 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4671 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4673 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4675 float *pos = new float;
4678 _line->set_data ("position", pos);
4684 FeatureLineDrag::finished (GdkEvent*, bool)
4686 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4687 _arv->update_transient(_before, _before);
4691 FeatureLineDrag::aborted (bool)
4696 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4698 , _vertical_only (false)
4700 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4704 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4706 Drag::start_grab (event);
4707 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4711 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4718 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4720 framepos_t grab = grab_frame ();
4721 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4722 _editor->snap_to_with_modifier (grab, event);
4724 grab = raw_grab_frame ();
4727 /* base start and end on initial click position */
4737 if (current_pointer_y() < grab_y()) {
4738 y1 = current_pointer_y();
4741 y2 = current_pointer_y();
4745 if (start != end || y1 != y2) {
4747 double x1 = _editor->sample_to_pixel (start);
4748 double x2 = _editor->sample_to_pixel (end);
4749 const double min_dimension = 2.0;
4751 if (_vertical_only) {
4752 /* fixed 10 pixel width */
4756 x2 = min (x1 - min_dimension, x2);
4758 x2 = max (x1 + min_dimension, x2);
4763 y2 = min (y1 - min_dimension, y2);
4765 y2 = max (y1 + min_dimension, y2);
4768 /* translate rect into item space and set */
4770 ArdourCanvas::Rect r (x1, y1, x2, y2);
4772 /* this drag is a _trackview_only == true drag, so the y1 and
4773 * y2 (computed using current_pointer_y() and grab_y()) will be
4774 * relative to the top of the trackview group). The
4775 * rubberband rect has the same parent/scroll offset as the
4776 * the trackview group, so we can use the "r" rect directly
4777 * to set the shape of the rubberband.
4780 _editor->rubberband_rect->set (r);
4781 _editor->rubberband_rect->show();
4782 _editor->rubberband_rect->raise_to_top();
4784 show_verbose_cursor_time (pf);
4786 do_select_things (event, true);
4791 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4795 framepos_t grab = grab_frame ();
4796 framepos_t lpf = last_pointer_frame ();
4798 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4799 grab = raw_grab_frame ();
4800 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4814 if (current_pointer_y() < grab_y()) {
4815 y1 = current_pointer_y();
4818 y2 = current_pointer_y();
4822 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4826 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4828 if (movement_occurred) {
4830 motion (event, false);
4831 do_select_things (event, false);
4837 bool do_deselect = true;
4838 MidiTimeAxisView* mtv;
4840 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4842 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4843 /* nothing selected */
4844 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4845 do_deselect = false;
4849 /* do not deselect if Primary or Tertiary (toggle-select or
4850 * extend-select are pressed.
4853 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4854 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4861 _editor->rubberband_rect->hide();
4865 RubberbandSelectDrag::aborted (bool)
4867 _editor->rubberband_rect->hide ();
4870 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4871 : RegionDrag (e, i, p, v)
4873 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4877 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4879 Drag::start_grab (event, cursor);
4881 _editor->get_selection().add (_primary);
4883 framepos_t where = _primary->region()->position();
4884 setup_snap_delta (where);
4886 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4890 TimeFXDrag::motion (GdkEvent* event, bool)
4892 RegionView* rv = _primary;
4893 StreamView* cv = rv->get_time_axis_view().view ();
4895 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4896 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4897 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4898 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4899 _editor->snap_to_with_modifier (pf, event);
4900 pf -= snap_delta (event->button.state);
4902 if (pf > rv->region()->position()) {
4903 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4906 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4910 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4912 /* this may have been a single click, no drag. We still want the dialog
4913 to show up in that case, so that the user can manually edit the
4914 parameters for the timestretch.
4917 float fraction = 1.0;
4919 if (movement_occurred) {
4921 motion (event, false);
4923 _primary->get_time_axis_view().hide_timestretch ();
4925 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4927 if (adjusted_frame_pos < _primary->region()->position()) {
4928 /* backwards drag of the left edge - not usable */
4932 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4934 fraction = (double) newlen / (double) _primary->region()->length();
4936 #ifndef USE_RUBBERBAND
4937 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4938 if (_primary->region()->data_type() == DataType::AUDIO) {
4939 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4944 if (!_editor->get_selection().regions.empty()) {
4945 /* primary will already be included in the selection, and edit
4946 group shared editing will propagate selection across
4947 equivalent regions, so just use the current region
4951 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4952 error << _("An error occurred while executing time stretch operation") << endmsg;
4958 TimeFXDrag::aborted (bool)
4960 _primary->get_time_axis_view().hide_timestretch ();
4963 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4966 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4970 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4972 Drag::start_grab (event);
4976 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4978 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4982 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4984 if (movement_occurred && _editor->session()) {
4985 /* make sure we stop */
4986 _editor->session()->request_transport_speed (0.0);
4991 ScrubDrag::aborted (bool)
4996 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5000 , _time_selection_at_start (!_editor->get_selection().time.empty())
5002 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5004 if (_time_selection_at_start) {
5005 start_at_start = _editor->get_selection().time.start();
5006 end_at_start = _editor->get_selection().time.end_frame();
5011 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5013 if (_editor->session() == 0) {
5017 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5019 switch (_operation) {
5020 case CreateSelection:
5021 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5026 cursor = _editor->cursors()->selector;
5027 Drag::start_grab (event, cursor);
5030 case SelectionStartTrim:
5031 if (_editor->clicked_axisview) {
5032 _editor->clicked_axisview->order_selection_trims (_item, true);
5034 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5037 case SelectionEndTrim:
5038 if (_editor->clicked_axisview) {
5039 _editor->clicked_axisview->order_selection_trims (_item, false);
5041 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5045 Drag::start_grab (event, cursor);
5048 case SelectionExtend:
5049 Drag::start_grab (event, cursor);
5053 if (_operation == SelectionMove) {
5054 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5056 show_verbose_cursor_time (adjusted_current_frame (event));
5061 SelectionDrag::setup_pointer_frame_offset ()
5063 switch (_operation) {
5064 case CreateSelection:
5065 _pointer_frame_offset = 0;
5068 case SelectionStartTrim:
5070 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5073 case SelectionEndTrim:
5074 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5077 case SelectionExtend:
5083 SelectionDrag::motion (GdkEvent* event, bool first_move)
5085 framepos_t start = 0;
5087 framecnt_t length = 0;
5088 framecnt_t distance = 0;
5090 framepos_t const pending_position = adjusted_current_frame (event);
5092 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5096 switch (_operation) {
5097 case CreateSelection:
5099 framepos_t grab = grab_frame ();
5102 grab = adjusted_current_frame (event, false);
5103 if (grab < pending_position) {
5104 _editor->snap_to (grab, RoundDownMaybe);
5106 _editor->snap_to (grab, RoundUpMaybe);
5110 if (pending_position < grab) {
5111 start = pending_position;
5114 end = pending_position;
5118 /* first drag: Either add to the selection
5119 or create a new selection
5126 /* adding to the selection */
5127 _editor->set_selected_track_as_side_effect (Selection::Add);
5128 _editor->clicked_selection = _editor->selection->add (start, end);
5135 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5136 _editor->set_selected_track_as_side_effect (Selection::Set);
5139 _editor->clicked_selection = _editor->selection->set (start, end);
5143 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5144 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5145 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5147 _editor->selection->add (atest);
5151 /* select all tracks within the rectangle that we've marked out so far */
5152 TrackViewList new_selection;
5153 TrackViewList& all_tracks (_editor->track_views);
5155 ArdourCanvas::Coord const top = grab_y();
5156 ArdourCanvas::Coord const bottom = current_pointer_y();
5158 if (top >= 0 && bottom >= 0) {
5160 //first, find the tracks that are covered in the y range selection
5161 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5162 if ((*i)->covered_by_y_range (top, bottom)) {
5163 new_selection.push_back (*i);
5167 //now find any tracks that are GROUPED with the tracks we selected
5168 TrackViewList grouped_add = new_selection;
5169 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5170 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5171 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5172 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5173 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5174 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5175 grouped_add.push_back (*j);
5180 //now compare our list with the current selection, and add or remove as necessary
5181 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5182 TrackViewList tracks_to_add;
5183 TrackViewList tracks_to_remove;
5184 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5185 if ( !_editor->selection->tracks.contains ( *i ) )
5186 tracks_to_add.push_back ( *i );
5187 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5188 if ( !grouped_add.contains ( *i ) )
5189 tracks_to_remove.push_back ( *i );
5190 _editor->selection->add(tracks_to_add);
5191 _editor->selection->remove(tracks_to_remove);
5197 case SelectionStartTrim:
5199 end = _editor->selection->time[_editor->clicked_selection].end;
5201 if (pending_position > end) {
5204 start = pending_position;
5208 case SelectionEndTrim:
5210 start = _editor->selection->time[_editor->clicked_selection].start;
5212 if (pending_position < start) {
5215 end = pending_position;
5222 start = _editor->selection->time[_editor->clicked_selection].start;
5223 end = _editor->selection->time[_editor->clicked_selection].end;
5225 length = end - start;
5226 distance = pending_position - start;
5227 start = pending_position;
5228 _editor->snap_to (start);
5230 end = start + length;
5234 case SelectionExtend:
5239 switch (_operation) {
5241 if (_time_selection_at_start) {
5242 _editor->selection->move_time (distance);
5246 _editor->selection->replace (_editor->clicked_selection, start, end);
5250 if (_operation == SelectionMove) {
5251 show_verbose_cursor_time(start);
5253 show_verbose_cursor_time(pending_position);
5258 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5260 Session* s = _editor->session();
5262 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5263 if (movement_occurred) {
5264 motion (event, false);
5265 /* XXX this is not object-oriented programming at all. ick */
5266 if (_editor->selection->time.consolidate()) {
5267 _editor->selection->TimeChanged ();
5270 /* XXX what if its a music time selection? */
5272 if (s->get_play_range() && s->transport_rolling()) {
5273 s->request_play_range (&_editor->selection->time, true);
5274 } else if (!s->config.get_external_sync()) {
5275 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5276 if (_operation == SelectionEndTrim)
5277 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5279 s->request_locate (_editor->get_selection().time.start());
5283 if (_editor->get_selection().time.length() != 0) {
5284 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5286 s->clear_range_selection ();
5291 /* just a click, no pointer movement.
5294 if (_operation == SelectionExtend) {
5295 if (_time_selection_at_start) {
5296 framepos_t pos = adjusted_current_frame (event, false);
5297 framepos_t start = min (pos, start_at_start);
5298 framepos_t end = max (pos, end_at_start);
5299 _editor->selection->set (start, end);
5302 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5303 if (_editor->clicked_selection) {
5304 _editor->selection->remove (_editor->clicked_selection);
5307 if (!_editor->clicked_selection) {
5308 _editor->selection->clear_time();
5313 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5314 _editor->selection->set (_editor->clicked_axisview);
5317 if (s && s->get_play_range () && s->transport_rolling()) {
5318 s->request_stop (false, false);
5323 _editor->stop_canvas_autoscroll ();
5324 _editor->clicked_selection = 0;
5325 _editor->commit_reversible_selection_op ();
5329 SelectionDrag::aborted (bool)
5334 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5335 : Drag (e, i, false),
5339 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5341 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5342 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5343 physical_screen_height (_editor->current_toplevel()->get_window())));
5344 _drag_rect->hide ();
5346 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5347 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5350 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5352 /* normal canvas items will be cleaned up when their parent group is deleted. But
5353 this item is created as the child of a long-lived parent group, and so we
5354 need to explicitly delete it.
5360 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5362 if (_editor->session() == 0) {
5366 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5368 if (!_editor->temp_location) {
5369 _editor->temp_location = new Location (*_editor->session());
5372 switch (_operation) {
5373 case CreateSkipMarker:
5374 case CreateRangeMarker:
5375 case CreateTransportMarker:
5376 case CreateCDMarker:
5378 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5383 cursor = _editor->cursors()->selector;
5387 Drag::start_grab (event, cursor);
5389 show_verbose_cursor_time (adjusted_current_frame (event));
5393 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5395 framepos_t start = 0;
5397 ArdourCanvas::Rectangle *crect;
5399 switch (_operation) {
5400 case CreateSkipMarker:
5401 crect = _editor->range_bar_drag_rect;
5403 case CreateRangeMarker:
5404 crect = _editor->range_bar_drag_rect;
5406 case CreateTransportMarker:
5407 crect = _editor->transport_bar_drag_rect;
5409 case CreateCDMarker:
5410 crect = _editor->cd_marker_bar_drag_rect;
5413 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5418 framepos_t const pf = adjusted_current_frame (event);
5420 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5421 framepos_t grab = grab_frame ();
5422 _editor->snap_to (grab);
5424 if (pf < grab_frame()) {
5432 /* first drag: Either add to the selection
5433 or create a new selection.
5438 _editor->temp_location->set (start, end);
5442 update_item (_editor->temp_location);
5444 //_drag_rect->raise_to_top();
5450 _editor->temp_location->set (start, end);
5452 double x1 = _editor->sample_to_pixel (start);
5453 double x2 = _editor->sample_to_pixel (end);
5457 update_item (_editor->temp_location);
5460 show_verbose_cursor_time (pf);
5465 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5467 Location * newloc = 0;
5471 if (movement_occurred) {
5472 motion (event, false);
5475 switch (_operation) {
5476 case CreateSkipMarker:
5477 case CreateRangeMarker:
5478 case CreateCDMarker:
5480 XMLNode &before = _editor->session()->locations()->get_state();
5481 if (_operation == CreateSkipMarker) {
5482 _editor->begin_reversible_command (_("new skip marker"));
5483 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5484 flags = Location::IsRangeMarker | Location::IsSkip;
5485 _editor->range_bar_drag_rect->hide();
5486 } else if (_operation == CreateCDMarker) {
5487 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5488 _editor->begin_reversible_command (_("new CD marker"));
5489 flags = Location::IsRangeMarker | Location::IsCDMarker;
5490 _editor->cd_marker_bar_drag_rect->hide();
5492 _editor->begin_reversible_command (_("new skip marker"));
5493 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5494 flags = Location::IsRangeMarker;
5495 _editor->range_bar_drag_rect->hide();
5497 newloc = new Location (
5498 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5501 _editor->session()->locations()->add (newloc, true);
5502 XMLNode &after = _editor->session()->locations()->get_state();
5503 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5504 _editor->commit_reversible_command ();
5508 case CreateTransportMarker:
5509 // popup menu to pick loop or punch
5510 _editor->new_transport_marker_context_menu (&event->button, _item);
5516 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5518 if (_operation == CreateTransportMarker) {
5520 /* didn't drag, so just locate */
5522 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5524 } else if (_operation == CreateCDMarker) {
5526 /* didn't drag, but mark is already created so do
5529 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5534 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5536 if (end == max_framepos) {
5537 end = _editor->session()->current_end_frame ();
5540 if (start == max_framepos) {
5541 start = _editor->session()->current_start_frame ();
5544 switch (_editor->mouse_mode) {
5546 /* find the two markers on either side and then make the selection from it */
5547 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5551 /* find the two markers on either side of the click and make the range out of it */
5552 _editor->selection->set (start, end);
5561 _editor->stop_canvas_autoscroll ();
5565 RangeMarkerBarDrag::aborted (bool movement_occurred)
5567 if (movement_occurred) {
5568 _drag_rect->hide ();
5573 RangeMarkerBarDrag::update_item (Location* location)
5575 double const x1 = _editor->sample_to_pixel (location->start());
5576 double const x2 = _editor->sample_to_pixel (location->end());
5578 _drag_rect->set_x0 (x1);
5579 _drag_rect->set_x1 (x2);
5582 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5584 , _cumulative_dx (0)
5585 , _cumulative_dy (0)
5586 , _was_selected (false)
5588 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5590 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5592 _region = &_primary->region_view ();
5593 _note_height = _region->midi_stream_view()->note_height ();
5597 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5599 Drag::start_grab (event);
5600 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5602 if (!(_was_selected = _primary->selected())) {
5604 /* tertiary-click means extend selection - we'll do that on button release,
5605 so don't add it here, because otherwise we make it hard to figure
5606 out the "extend-to" range.
5609 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5612 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5615 _region->note_selected (_primary, true);
5617 _editor->get_selection().clear_points();
5618 _region->unique_select (_primary);
5624 /** @return Current total drag x change in frames */
5626 NoteDrag::total_dx (const guint state) const
5628 if (_x_constrained) {
5631 TempoMap& map (_editor->session()->tempo_map());
5634 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5636 /* primary note time */
5637 double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5638 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5640 /* new time of the primary note in session frames */
5641 frameoffset_t st = n + dx + snap_delta (state);
5643 framepos_t const rp = _region->region()->position ();
5645 /* prevent the note being dragged earlier than the region's position */
5648 /* possibly snap and return corresponding delta */
5652 if (ArdourKeyboard::indicates_snap (state)) {
5653 if (_editor->snap_mode () != SnapOff) {
5657 if (_editor->snap_mode () == SnapOff) {
5659 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5660 if (ArdourKeyboard::indicates_snap_delta (state)) {
5668 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5669 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5671 ret = st - n - snap_delta (state);
5676 /** @return Current total drag y change in note number */
5678 NoteDrag::total_dy () const
5680 if (_y_constrained) {
5684 double const y = _region->midi_view()->y_position ();
5685 /* new current note */
5686 uint8_t n = _region->y_to_note (current_pointer_y () - y);
5688 MidiStreamView* msv = _region->midi_stream_view ();
5689 n = max (msv->lowest_note(), n);
5690 n = min (msv->highest_note(), n);
5691 /* and work out delta */
5692 return n - _region->y_to_note (grab_y() - y);
5696 NoteDrag::motion (GdkEvent * event, bool)
5698 /* Total change in x and y since the start of the drag */
5699 frameoffset_t const dx = total_dx (event->button.state);
5700 int8_t const dy = total_dy ();
5702 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5703 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5704 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5707 _cumulative_dx += tdx;
5708 _cumulative_dy += tdy;
5710 int8_t note_delta = total_dy();
5713 _region->move_selection (tdx, tdy, note_delta);
5715 /* the new note value may be the same as the old one, but we
5716 * don't know what that means because the selection may have
5717 * involved more than one note and we might be doing something
5718 * odd with them. so show the note value anyway, always.
5721 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5723 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5729 NoteDrag::finished (GdkEvent* ev, bool moved)
5732 /* no motion - select note */
5734 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5735 _editor->current_mouse_mode() == Editing::MouseDraw) {
5737 bool changed = false;
5739 if (_was_selected) {
5740 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5742 _region->note_deselected (_primary);
5745 _editor->get_selection().clear_points();
5746 _region->unique_select (_primary);
5750 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5751 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5753 if (!extend && !add && _region->selection_size() > 1) {
5754 _editor->get_selection().clear_points();
5755 _region->unique_select (_primary);
5757 } else if (extend) {
5758 _region->note_selected (_primary, true, true);
5761 /* it was added during button press */
5768 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5769 _editor->commit_reversible_selection_op();
5773 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5778 NoteDrag::aborted (bool)
5783 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5784 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5785 : Drag (editor, atv->base_item ())
5787 , _y_origin (atv->y_position())
5788 , _nothing_to_drag (false)
5790 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5791 setup (atv->lines ());
5794 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5795 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5796 : Drag (editor, rv->get_canvas_group ())
5798 , _y_origin (rv->get_time_axis_view().y_position())
5799 , _nothing_to_drag (false)
5802 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5804 list<boost::shared_ptr<AutomationLine> > lines;
5806 AudioRegionView* audio_view;
5807 AutomationRegionView* automation_view;
5808 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5809 lines.push_back (audio_view->get_gain_line ());
5810 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5811 lines.push_back (automation_view->line ());
5814 error << _("Automation range drag created for invalid region type") << endmsg;
5820 /** @param lines AutomationLines to drag.
5821 * @param offset Offset from the session start to the points in the AutomationLines.
5824 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5826 /* find the lines that overlap the ranges being dragged */
5827 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5828 while (i != lines.end ()) {
5829 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5832 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5834 /* check this range against all the AudioRanges that we are using */
5835 list<AudioRange>::const_iterator k = _ranges.begin ();
5836 while (k != _ranges.end()) {
5837 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5843 /* add it to our list if it overlaps at all */
5844 if (k != _ranges.end()) {
5849 _lines.push_back (n);
5855 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5859 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5861 return 1.0 - ((global_y - _y_origin) / line->height());
5865 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5867 const double v = list->eval(x);
5868 return _integral ? rint(v) : v;
5872 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5874 Drag::start_grab (event, cursor);
5876 /* Get line states before we start changing things */
5877 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5878 i->state = &i->line->get_state ();
5879 i->original_fraction = y_fraction (i->line, current_pointer_y());
5882 if (_ranges.empty()) {
5884 /* No selected time ranges: drag all points */
5885 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5886 uint32_t const N = i->line->npoints ();
5887 for (uint32_t j = 0; j < N; ++j) {
5888 i->points.push_back (i->line->nth (j));
5894 if (_nothing_to_drag) {
5900 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5902 if (_nothing_to_drag && !first_move) {
5907 _editor->begin_reversible_command (_("automation range move"));
5909 if (!_ranges.empty()) {
5911 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5913 framecnt_t const half = (i->start + i->end) / 2;
5915 /* find the line that this audio range starts in */
5916 list<Line>::iterator j = _lines.begin();
5917 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5921 if (j != _lines.end()) {
5922 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5924 /* j is the line that this audio range starts in; fade into it;
5925 64 samples length plucked out of thin air.
5928 framepos_t a = i->start + 64;
5933 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5934 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5936 XMLNode &before = the_list->get_state();
5937 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5938 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5940 if (add_p || add_q) {
5941 _editor->session()->add_command (
5942 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5946 /* same thing for the end */
5949 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5953 if (j != _lines.end()) {
5954 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5956 /* j is the line that this audio range starts in; fade out of it;
5957 64 samples length plucked out of thin air.
5960 framepos_t b = i->end - 64;
5965 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5966 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5968 XMLNode &before = the_list->get_state();
5969 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5970 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5972 if (add_p || add_q) {
5973 _editor->session()->add_command (
5974 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5979 _nothing_to_drag = true;
5981 /* Find all the points that should be dragged and put them in the relevant
5982 points lists in the Line structs.
5985 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5987 uint32_t const N = i->line->npoints ();
5988 for (uint32_t j = 0; j < N; ++j) {
5990 /* here's a control point on this line */
5991 ControlPoint* p = i->line->nth (j);
5992 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5994 /* see if it's inside a range */
5995 list<AudioRange>::const_iterator k = _ranges.begin ();
5996 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6000 if (k != _ranges.end()) {
6001 /* dragging this point */
6002 _nothing_to_drag = false;
6003 i->points.push_back (p);
6009 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6010 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6014 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6015 float const f = y_fraction (l->line, current_pointer_y());
6016 /* we are ignoring x position for this drag, so we can just pass in anything */
6017 pair<double, float> result;
6019 result = l->line->drag_motion (0, f, true, false, ignored);
6020 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6025 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6027 if (_nothing_to_drag || !motion_occurred) {
6031 motion (event, false);
6032 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6033 i->line->end_drag (false, 0);
6036 _editor->commit_reversible_command ();
6040 AutomationRangeDrag::aborted (bool)
6042 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6047 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6049 , initial_time_axis_view (itav)
6051 /* note that time_axis_view may be null if the regionview was created
6052 * as part of a copy operation.
6054 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6055 layer = v->region()->layer ();
6056 initial_y = v->get_canvas_group()->position().y;
6057 initial_playlist = v->region()->playlist ();
6058 initial_position = v->region()->position ();
6059 initial_end = v->region()->position () + v->region()->length ();
6062 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6063 : Drag (e, i->canvas_item ())
6066 , _cumulative_dx (0)
6068 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6069 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6074 PatchChangeDrag::motion (GdkEvent* ev, bool)
6076 framepos_t f = adjusted_current_frame (ev);
6077 boost::shared_ptr<Region> r = _region_view->region ();
6078 f = max (f, r->position ());
6079 f = min (f, r->last_frame ());
6081 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6082 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6083 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6084 _cumulative_dx = dxu;
6088 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6090 if (!movement_occurred) {
6091 if (was_double_click()) {
6092 _region_view->edit_patch_change (_patch_change);
6097 boost::shared_ptr<Region> r (_region_view->region ());
6098 framepos_t f = adjusted_current_frame (ev);
6099 f = max (f, r->position ());
6100 f = min (f, r->last_frame ());
6102 _region_view->move_patch_change (
6104 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6109 PatchChangeDrag::aborted (bool)
6111 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6115 PatchChangeDrag::setup_pointer_frame_offset ()
6117 boost::shared_ptr<Region> region = _region_view->region ();
6118 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6121 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6122 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6129 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6131 _region_view->update_drag_selection (
6133 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6137 MidiRubberbandSelectDrag::deselect_things ()
6142 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6143 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6146 _vertical_only = true;
6150 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6152 double const y = _region_view->midi_view()->y_position ();
6154 y1 = max (0.0, y1 - y);
6155 y2 = max (0.0, y2 - y);
6157 _region_view->update_vertical_drag_selection (
6160 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6165 MidiVerticalSelectDrag::deselect_things ()
6170 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6171 : RubberbandSelectDrag (e, i)
6177 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6179 if (drag_in_progress) {
6180 /* We just want to select things at the end of the drag, not during it */
6184 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6186 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6188 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6190 _editor->commit_reversible_selection_op ();
6194 EditorRubberbandSelectDrag::deselect_things ()
6196 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6198 _editor->selection->clear_tracks();
6199 _editor->selection->clear_regions();
6200 _editor->selection->clear_points ();
6201 _editor->selection->clear_lines ();
6202 _editor->selection->clear_midi_notes ();
6204 _editor->commit_reversible_selection_op();
6207 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6212 _note[0] = _note[1] = 0;
6215 NoteCreateDrag::~NoteCreateDrag ()
6221 NoteCreateDrag::grid_frames (framepos_t t) const
6224 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6225 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6227 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6228 - _region_view->region_beats_to_region_frames (t_beats);
6232 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6234 Drag::start_grab (event, cursor);
6236 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6237 TempoMap& map (_editor->session()->tempo_map());
6239 const framepos_t pf = _drags->current_pointer_frame ();
6240 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6242 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6244 double eqaf = map.exact_qn_at_frame (pf, divisions);
6246 if (divisions != 0) {
6248 const double qaf = map.quarter_note_at_frame (pf);
6250 /* Hack so that we always snap to the note that we are over, instead of snapping
6251 to the next one if we're more than halfway through the one we're over.
6254 const double rem = eqaf - qaf;
6256 eqaf -= grid_beats.to_double();
6260 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6261 /* minimum initial length is grid beats */
6262 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6264 double const x0 = _editor->sample_to_pixel (_note[0]);
6265 double const x1 = _editor->sample_to_pixel (_note[1]);
6266 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6268 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6269 _drag_rect->set_outline_all ();
6270 _drag_rect->set_outline_color (0xffffff99);
6271 _drag_rect->set_fill_color (0xffffff66);
6275 NoteCreateDrag::motion (GdkEvent* event, bool)
6277 TempoMap& map (_editor->session()->tempo_map());
6278 const framepos_t pf = _drags->current_pointer_frame ();
6279 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6280 double eqaf = map.exact_qn_at_frame (pf, divisions);
6282 if (divisions != 0) {
6284 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6286 const double qaf = map.quarter_note_at_frame (pf);
6287 /* Hack so that we always snap to the note that we are over, instead of snapping
6288 to the next one if we're more than halfway through the one we're over.
6291 const double rem = eqaf - qaf;
6293 eqaf -= grid_beats.to_double();
6296 eqaf += grid_beats.to_double();
6298 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6300 double const x0 = _editor->sample_to_pixel (_note[0]);
6301 double const x1 = _editor->sample_to_pixel (_note[1]);
6302 _drag_rect->set_x0 (std::min(x0, x1));
6303 _drag_rect->set_x1 (std::max(x0, x1));
6307 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6309 /* we create a note even if there was no movement */
6310 framepos_t const start = min (_note[0], _note[1]);
6311 framepos_t const start_sess_rel = start + _region_view->region()->position();
6312 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6313 framecnt_t const g = grid_frames (start);
6315 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6319 TempoMap& map (_editor->session()->tempo_map());
6320 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6321 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6323 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6327 NoteCreateDrag::y_to_region (double y) const
6330 _region_view->get_canvas_group()->canvas_to_item (x, y);
6335 NoteCreateDrag::aborted (bool)
6340 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6348 HitCreateDrag::~HitCreateDrag ()
6353 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6355 Drag::start_grab (event, cursor);
6357 TempoMap& map (_editor->session()->tempo_map());
6359 const framepos_t pf = _drags->current_pointer_frame ();
6360 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6362 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6364 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6366 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6370 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6371 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6373 Evoral::Beats length = _region_view->get_grid_beats (pf);
6375 _region_view->create_note_at (start, y, length, event->button.state, false);
6382 HitCreateDrag::motion (GdkEvent* event, bool)
6384 TempoMap& map (_editor->session()->tempo_map());
6386 const framepos_t pf = _drags->current_pointer_frame ();
6387 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6389 if (divisions == 0) {
6393 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6394 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6395 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6397 if (_last_pos == start && y == _last_y) {
6401 Evoral::Beats length = _region_view->get_grid_beats (pf);
6403 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6404 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6408 _region_view->create_note_at (start, y, length, event->button.state, false);
6415 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6421 HitCreateDrag::y_to_region (double y) const
6424 _region_view->get_canvas_group()->canvas_to_item (x, y);
6429 HitCreateDrag::aborted (bool)
6434 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6439 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6443 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6445 Drag::start_grab (event, cursor);
6449 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6455 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6458 distance = _drags->current_pointer_x() - grab_x();
6459 len = ar->fade_in()->back()->when;
6461 distance = grab_x() - _drags->current_pointer_x();
6462 len = ar->fade_out()->back()->when;
6465 /* how long should it be ? */
6467 new_length = len + _editor->pixel_to_sample (distance);
6469 /* now check with the region that this is legal */
6471 new_length = ar->verify_xfade_bounds (new_length, start);
6474 arv->reset_fade_in_shape_width (ar, new_length);
6476 arv->reset_fade_out_shape_width (ar, new_length);
6481 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6487 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6490 distance = _drags->current_pointer_x() - grab_x();
6491 len = ar->fade_in()->back()->when;
6493 distance = grab_x() - _drags->current_pointer_x();
6494 len = ar->fade_out()->back()->when;
6497 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6499 _editor->begin_reversible_command ("xfade trim");
6500 ar->playlist()->clear_owned_changes ();
6503 ar->set_fade_in_length (new_length);
6505 ar->set_fade_out_length (new_length);
6508 /* Adjusting the xfade may affect other regions in the playlist, so we need
6509 to get undo Commands from the whole playlist rather than just the
6513 vector<Command*> cmds;
6514 ar->playlist()->rdiff (cmds);
6515 _editor->session()->add_commands (cmds);
6516 _editor->commit_reversible_command ();
6521 CrossfadeEdgeDrag::aborted (bool)
6524 // arv->redraw_start_xfade ();
6526 // arv->redraw_end_xfade ();
6530 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6531 : Drag (e, item, true)
6532 , line (new EditorCursor (*e))
6534 line->set_position (pos);
6536 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6539 RegionCutDrag::~RegionCutDrag ()
6545 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6547 Drag::start_grab (event, c);
6548 motion (event, false);
6552 RegionCutDrag::motion (GdkEvent* event, bool)
6554 framepos_t pos = _drags->current_pointer_frame();
6555 _editor->snap_to_with_modifier (pos, event);
6557 line->set_position (pos);
6561 RegionCutDrag::finished (GdkEvent* event, bool)
6563 _editor->get_track_canvas()->canvas()->re_enter();
6565 framepos_t pos = _drags->current_pointer_frame();
6566 _editor->snap_to_with_modifier (pos, event);
6570 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6576 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state),
6581 RegionCutDrag::aborted (bool)
6585 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6586 : Drag (e, item, true)
6591 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6593 Drag::start_grab (event, c);
6595 framepos_t where = _editor->canvas_event_sample(event);
6597 _editor->_dragging_playhead = true;
6599 _editor->playhead_cursor->set_position (where);
6603 RulerZoomDrag::motion (GdkEvent* event, bool)
6605 framepos_t where = _editor->canvas_event_sample(event);
6607 _editor->playhead_cursor->set_position (where);
6609 const double movement_limit = 20.0;
6610 const double scale = 1.08;
6611 const double y_delta = last_pointer_y() - current_pointer_y();
6613 if (y_delta > 0 && y_delta < movement_limit) {
6614 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6615 } else if (y_delta < 0 && y_delta > -movement_limit) {
6616 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6621 RulerZoomDrag::finished (GdkEvent*, bool)
6623 _editor->_dragging_playhead = false;
6625 Session* s = _editor->session ();
6627 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6628 _editor->_pending_locate_request = true;
6634 RulerZoomDrag::aborted (bool)