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;
1298 if (rv == _primary) {
1299 region_copy = RegionFactory::create (original, true
1300 , current_music_divisor (original->position(), event->button.state));
1302 region_copy = RegionFactory::create (original, true, 0);
1305 /* need to set this so that the drop zone code can work. This doesn't
1306 actually put the region into the playlist, but just sets a weak pointer
1309 region_copy->set_playlist (original->playlist());
1313 boost::shared_ptr<AudioRegion> audioregion_copy
1314 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1316 nrv = new AudioRegionView (*arv, audioregion_copy);
1318 boost::shared_ptr<MidiRegion> midiregion_copy
1319 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1320 nrv = new MidiRegionView (*mrv, midiregion_copy);
1325 nrv->get_canvas_group()->show ();
1326 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1328 /* swap _primary to the copy */
1330 if (rv == _primary) {
1334 /* ..and deselect the one we copied */
1336 rv->set_selected (false);
1339 if (!new_regionviews.empty()) {
1341 /* reflect the fact that we are dragging the copies */
1343 _views = new_regionviews;
1345 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1348 } else if (!_copy && first_move) {
1349 if (_x_constrained && !_brushing) {
1350 _editor->begin_reversible_command (_("fixed time region drag"));
1351 } else if (!_brushing) {
1352 _editor->begin_reversible_command (Operations::region_drag);
1353 } else if (_brushing) {
1354 _editor->begin_reversible_command (Operations::drag_region_brush);
1357 RegionMotionDrag::motion (event, first_move);
1361 RegionMotionDrag::finished (GdkEvent *, bool)
1363 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1364 if (!(*i)->view()) {
1368 if ((*i)->view()->layer_display() == Expanded) {
1369 (*i)->view()->set_layer_display (Stacked);
1375 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1377 RegionMotionDrag::finished (ev, movement_occurred);
1379 if (!movement_occurred) {
1383 if (was_double_click() && !_views.empty()) {
1384 DraggingView dv = _views.front();
1385 _editor->edit_region (dv.view);
1391 assert (!_views.empty ());
1393 /* We might have hidden region views so that they weren't visible during the drag
1394 (when they have been reparented). Now everything can be shown again, as region
1395 views are back in their track parent groups.
1397 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1398 i->view->get_canvas_group()->show ();
1401 bool const changed_position = (_last_frame_position != _primary->region()->position());
1402 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1403 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1425 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1429 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1431 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1434 TimeAxisView* tav = 0;
1436 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1437 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1438 uint32_t output_chan = region->n_channels();
1439 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1440 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1442 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1443 tav =_editor->axis_view_from_stripable (audio_tracks.front());
1445 ChanCount one_midi_port (DataType::MIDI, 1);
1446 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1447 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1448 (ARDOUR::Plugin::PresetRecord*) 0,
1449 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1450 tav = _editor->axis_view_from_stripable (midi_tracks.front());
1454 tav->set_height (original->current_height());
1457 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1460 return dynamic_cast<RouteTimeAxisView*> (tav);
1464 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state)
1466 RegionSelection new_views;
1467 PlaylistSet modified_playlists;
1468 RouteTimeAxisView* new_time_axis_view = 0;
1471 /* all changes were made during motion event handlers */
1473 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1477 _editor->commit_reversible_command ();
1481 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1482 PlaylistMapping playlist_mapping;
1484 /* insert the regions into their new playlists */
1485 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1487 RouteTimeAxisView* dest_rtv = 0;
1489 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1495 if (changed_position && !_x_constrained) {
1496 where = i->view->region()->position() - drag_delta;
1498 where = i->view->region()->position();
1501 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1502 /* dragged to drop zone */
1504 PlaylistMapping::iterator pm;
1506 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1507 /* first region from this original playlist: create a new track */
1508 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1509 if(!new_time_axis_view) {
1513 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1514 dest_rtv = new_time_axis_view;
1516 /* we already created a new track for regions from this playlist, use it */
1517 dest_rtv = pm->second;
1520 /* destination time axis view is the one we dragged to */
1521 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1524 if (dest_rtv != 0) {
1525 RegionView* new_view;
1526 if (i->view == _primary) {
1527 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1528 modified_playlists, current_music_divisor (where, ev_state));
1530 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1531 modified_playlists, 0);
1534 if (new_view != 0) {
1535 new_views.push_back (new_view);
1539 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1540 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1543 list<DraggingView>::const_iterator next = i;
1549 /* If we've created new regions either by copying or moving
1550 to a new track, we want to replace the old selection with the new ones
1553 if (new_views.size() > 0) {
1554 _editor->selection->set (new_views);
1557 /* write commands for the accumulated diffs for all our modified playlists */
1558 add_stateful_diff_commands_for_playlists (modified_playlists);
1560 _editor->commit_reversible_command ();
1564 RegionMoveDrag::finished_no_copy (
1565 bool const changed_position,
1566 bool const changed_tracks,
1567 framecnt_t const drag_delta,
1568 int32_t const ev_state
1571 RegionSelection new_views;
1572 PlaylistSet modified_playlists;
1573 PlaylistSet frozen_playlists;
1574 set<RouteTimeAxisView*> views_to_update;
1575 RouteTimeAxisView* new_time_axis_view = 0;
1577 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1578 PlaylistMapping playlist_mapping;
1580 std::set<boost::shared_ptr<const Region> > uniq;
1581 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1583 RegionView* rv = i->view;
1584 RouteTimeAxisView* dest_rtv = 0;
1586 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1591 if (uniq.find (rv->region()) != uniq.end()) {
1592 /* prevent duplicate moves when selecting regions from shared playlists */
1596 uniq.insert(rv->region());
1598 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1599 /* dragged to drop zone */
1601 PlaylistMapping::iterator pm;
1603 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1604 /* first region from this original playlist: create a new track */
1605 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1606 if(!new_time_axis_view) { // New track creation failed
1610 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1611 dest_rtv = new_time_axis_view;
1613 /* we already created a new track for regions from this playlist, use it */
1614 dest_rtv = pm->second;
1618 /* destination time axis view is the one we dragged to */
1619 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1624 double const dest_layer = i->layer;
1626 views_to_update.insert (dest_rtv);
1630 if (changed_position && !_x_constrained) {
1631 where = rv->region()->position() - drag_delta;
1633 where = rv->region()->position();
1636 if (changed_tracks) {
1638 /* insert into new playlist */
1639 RegionView* new_view;
1640 if (rv == _primary) {
1641 new_view = insert_region_into_playlist (
1642 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1643 modified_playlists, current_music_divisor (where, ev_state)
1646 new_view = insert_region_into_playlist (
1647 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1648 modified_playlists, 0
1652 if (new_view == 0) {
1657 new_views.push_back (new_view);
1659 /* remove from old playlist */
1661 /* the region that used to be in the old playlist is not
1662 moved to the new one - we use a copy of it. as a result,
1663 any existing editor for the region should no longer be
1666 rv->hide_region_editor();
1669 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1673 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1675 /* this movement may result in a crossfade being modified, or a layering change,
1676 so we need to get undo data from the playlist as well as the region.
1679 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1681 playlist->clear_changes ();
1684 rv->region()->clear_changes ();
1687 motion on the same track. plonk the previously reparented region
1688 back to its original canvas group (its streamview).
1689 No need to do anything for copies as they are fake regions which will be deleted.
1692 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1693 rv->get_canvas_group()->set_y_position (i->initial_y);
1696 /* just change the model */
1697 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1698 playlist->set_layer (rv->region(), dest_layer);
1701 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1703 r = frozen_playlists.insert (playlist);
1706 playlist->freeze ();
1708 if (rv == _primary) {
1709 rv->region()->set_position (where, current_music_divisor (where, ev_state));
1711 rv->region()->set_position (where, 0);
1713 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1716 if (changed_tracks) {
1718 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1719 was selected in all of them, then removing it from a playlist will have removed all
1720 trace of it from _views (i.e. there were N regions selected, we removed 1,
1721 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1722 corresponding regionview, and _views is now empty).
1724 This could have invalidated any and all iterators into _views.
1726 The heuristic we use here is: if the region selection is empty, break out of the loop
1727 here. if the region selection is not empty, then restart the loop because we know that
1728 we must have removed at least the region(view) we've just been working on as well as any
1729 that we processed on previous iterations.
1731 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1732 we can just iterate.
1736 if (_views.empty()) {
1747 /* If we've created new regions either by copying or moving
1748 to a new track, we want to replace the old selection with the new ones
1751 if (new_views.size() > 0) {
1752 _editor->selection->set (new_views);
1755 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1759 /* write commands for the accumulated diffs for all our modified playlists */
1760 add_stateful_diff_commands_for_playlists (modified_playlists);
1761 /* applies to _brushing */
1762 _editor->commit_reversible_command ();
1764 /* We have futzed with the layering of canvas items on our streamviews.
1765 If any region changed layer, this will have resulted in the stream
1766 views being asked to set up their region views, and all will be well.
1767 If not, we might now have badly-ordered region views. Ask the StreamViews
1768 involved to sort themselves out, just in case.
1771 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1772 (*i)->view()->playlist_layered ((*i)->track ());
1776 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1777 * @param region Region to remove.
1778 * @param playlist playlist To remove from.
1779 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1780 * that clear_changes () is only called once per playlist.
1783 RegionMoveDrag::remove_region_from_playlist (
1784 boost::shared_ptr<Region> region,
1785 boost::shared_ptr<Playlist> playlist,
1786 PlaylistSet& modified_playlists
1789 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1792 playlist->clear_changes ();
1795 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1799 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1800 * clearing the playlist's diff history first if necessary.
1801 * @param region Region to insert.
1802 * @param dest_rtv Destination RouteTimeAxisView.
1803 * @param dest_layer Destination layer.
1804 * @param where Destination position.
1805 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1806 * that clear_changes () is only called once per playlist.
1807 * @return New RegionView, or 0 if no insert was performed.
1810 RegionMoveDrag::insert_region_into_playlist (
1811 boost::shared_ptr<Region> region,
1812 RouteTimeAxisView* dest_rtv,
1815 PlaylistSet& modified_playlists,
1816 const int32_t sub_num
1819 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1820 if (!dest_playlist) {
1824 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1825 _new_region_view = 0;
1826 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1828 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1829 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1831 dest_playlist->clear_changes ();
1833 dest_playlist->add_region (region, where, 1.0, false, sub_num);
1835 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1836 dest_playlist->set_layer (region, dest_layer);
1841 assert (_new_region_view);
1843 return _new_region_view;
1847 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1849 _new_region_view = rv;
1853 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1855 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1856 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1858 _editor->session()->add_command (c);
1867 RegionMoveDrag::aborted (bool movement_occurred)
1871 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1872 list<DraggingView>::const_iterator next = i;
1881 RegionMotionDrag::aborted (movement_occurred);
1886 RegionMotionDrag::aborted (bool)
1888 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1890 StreamView* sview = (*i)->view();
1893 if (sview->layer_display() == Expanded) {
1894 sview->set_layer_display (Stacked);
1899 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1900 RegionView* rv = i->view;
1901 TimeAxisView* tv = &(rv->get_time_axis_view ());
1902 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1904 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1905 rv->get_canvas_group()->set_y_position (0);
1907 rv->move (-_total_x_delta, 0);
1908 rv->set_height (rtv->view()->child_height ());
1912 /** @param b true to brush, otherwise false.
1913 * @param c true to make copies of the regions being moved, otherwise false.
1915 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1916 : RegionMotionDrag (e, i, p, v, b)
1918 , _new_region_view (0)
1920 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1923 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1924 if (rtv && rtv->is_track()) {
1925 speed = rtv->track()->speed ();
1928 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1932 RegionMoveDrag::setup_pointer_frame_offset ()
1934 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1937 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1938 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1940 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1942 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1943 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1945 _primary = v->view()->create_region_view (r, false, false);
1947 _primary->get_canvas_group()->show ();
1948 _primary->set_position (pos, 0);
1949 _views.push_back (DraggingView (_primary, this, v));
1951 _last_frame_position = pos;
1953 _item = _primary->get_canvas_group ();
1957 RegionInsertDrag::finished (GdkEvent * event, bool)
1959 int pos = _views.front().time_axis_view;
1960 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1962 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1964 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1965 _primary->get_canvas_group()->set_y_position (0);
1967 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1969 _editor->begin_reversible_command (Operations::insert_region);
1970 playlist->clear_changes ();
1971 playlist->add_region (_primary->region (), _last_frame_position);
1973 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1974 if (Config->get_edit_mode() == Ripple) {
1975 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1978 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1979 _editor->commit_reversible_command ();
1987 RegionInsertDrag::aborted (bool)
1994 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1995 : RegionMoveDrag (e, i, p, v, false, false)
1997 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2000 struct RegionSelectionByPosition {
2001 bool operator() (RegionView*a, RegionView* b) {
2002 return a->region()->position () < b->region()->position();
2007 RegionSpliceDrag::motion (GdkEvent* event, bool)
2009 /* Which trackview is this ? */
2011 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2012 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2014 /* The region motion is only processed if the pointer is over
2018 if (!tv || !tv->is_track()) {
2019 /* To make sure we hide the verbose canvas cursor when the mouse is
2020 not held over an audio track.
2022 _editor->verbose_cursor()->hide ();
2025 _editor->verbose_cursor()->show ();
2030 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2036 RegionSelection copy;
2037 _editor->selection->regions.by_position(copy);
2039 framepos_t const pf = adjusted_current_frame (event);
2041 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2043 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2049 boost::shared_ptr<Playlist> playlist;
2051 if ((playlist = atv->playlist()) == 0) {
2055 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2060 if (pf < (*i)->region()->last_frame() + 1) {
2064 if (pf > (*i)->region()->first_frame()) {
2070 playlist->shuffle ((*i)->region(), dir);
2075 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2077 RegionMoveDrag::finished (event, movement_occurred);
2081 RegionSpliceDrag::aborted (bool)
2091 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2094 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2096 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2097 RegionSelection to_ripple;
2098 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2099 if ((*i)->position() >= where) {
2100 to_ripple.push_back (rtv->view()->find_view(*i));
2104 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2105 if (!exclude.contains (*i)) {
2106 // the selection has already been added to _views
2108 if (drag_in_progress) {
2109 // do the same things that RegionMotionDrag::motion does when
2110 // first_move is true, for the region views that we're adding
2111 // to _views this time
2114 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2115 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2116 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2117 rvg->reparent (_editor->_drag_motion_group);
2119 // we only need to move in the y direction
2120 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2125 _views.push_back (DraggingView (*i, this, tav));
2131 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2134 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2135 // we added all the regions after the selection
2137 std::list<DraggingView>::iterator to_erase = i++;
2138 if (!_editor->selection->regions.contains (to_erase->view)) {
2139 // restore the non-selected regions to their original playlist & positions,
2140 // and then ripple them back by the length of the regions that were dragged away
2141 // do the same things as RegionMotionDrag::aborted
2143 RegionView *rv = to_erase->view;
2144 TimeAxisView* tv = &(rv->get_time_axis_view ());
2145 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2148 // plonk them back onto their own track
2149 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2150 rv->get_canvas_group()->set_y_position (0);
2154 // move the underlying region to match the view
2155 rv->region()->set_position (rv->region()->position() + amount);
2157 // restore the view to match the underlying region's original position
2158 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2161 rv->set_height (rtv->view()->child_height ());
2162 _views.erase (to_erase);
2168 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2170 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2172 return allow_moves_across_tracks;
2180 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2181 : RegionMoveDrag (e, i, p, v, false, false)
2183 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2184 // compute length of selection
2185 RegionSelection selected_regions = _editor->selection->regions;
2186 selection_length = selected_regions.end_frame() - selected_regions.start();
2188 // we'll only allow dragging to another track in ripple mode if all the regions
2189 // being dragged start off on the same track
2190 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2193 exclude = new RegionList;
2194 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2195 exclude->push_back((*i)->region());
2198 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2199 RegionSelection copy;
2200 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2202 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2203 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2205 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2206 // find ripple start point on each applicable playlist
2207 RegionView *first_selected_on_this_track = NULL;
2208 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2209 if ((*i)->region()->playlist() == (*pi)) {
2210 // region is on this playlist - it's the first, because they're sorted
2211 first_selected_on_this_track = *i;
2215 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2216 add_all_after_to_views (
2217 &first_selected_on_this_track->get_time_axis_view(),
2218 first_selected_on_this_track->region()->position(),
2219 selected_regions, false);
2222 if (allow_moves_across_tracks) {
2223 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2231 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2233 /* Which trackview is this ? */
2235 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2236 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2238 /* The region motion is only processed if the pointer is over
2242 if (!tv || !tv->is_track()) {
2243 /* To make sure we hide the verbose canvas cursor when the mouse is
2244 not held over an audiotrack.
2246 _editor->verbose_cursor()->hide ();
2250 framepos_t where = adjusted_current_frame (event);
2251 assert (where >= 0);
2253 double delta = compute_x_delta (event, &after);
2255 framecnt_t amount = _editor->pixel_to_sample (delta);
2257 if (allow_moves_across_tracks) {
2258 // all the originally selected regions were on the same track
2260 framecnt_t adjust = 0;
2261 if (prev_tav && tv != prev_tav) {
2262 // dragged onto a different track
2263 // remove the unselected regions from _views, restore them to their original positions
2264 // and add the regions after the drop point on the new playlist to _views instead.
2265 // undo the effect of rippling the previous playlist, and include the effect of removing
2266 // the dragged region(s) from this track
2268 remove_unselected_from_views (prev_amount, false);
2269 // ripple previous playlist according to the regions that have been removed onto the new playlist
2270 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2273 // move just the selected regions
2274 RegionMoveDrag::motion(event, first_move);
2276 // ensure that the ripple operation on the new playlist inserts selection_length time
2277 adjust = selection_length;
2278 // ripple the new current playlist
2279 tv->playlist()->ripple (where, amount+adjust, exclude);
2281 // add regions after point where drag entered this track to subsequent ripples
2282 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2285 // motion on same track
2286 RegionMoveDrag::motion(event, first_move);
2290 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2291 prev_position = where;
2293 // selection encompasses multiple tracks - just drag
2294 // cross-track drags are forbidden
2295 RegionMoveDrag::motion(event, first_move);
2298 if (!_x_constrained) {
2299 prev_amount += amount;
2302 _last_frame_position = after;
2306 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2308 if (!movement_occurred) {
2312 if (was_double_click() && !_views.empty()) {
2313 DraggingView dv = _views.front();
2314 _editor->edit_region (dv.view);
2320 _editor->begin_reversible_command(_("Ripple drag"));
2322 // remove the regions being rippled from the dragging view, updating them to
2323 // their new positions
2324 remove_unselected_from_views (prev_amount, true);
2326 if (allow_moves_across_tracks) {
2328 // if regions were dragged across tracks, we've rippled any later
2329 // regions on the track the regions were dragged off, so we need
2330 // to add the original track to the undo record
2331 orig_tav->playlist()->clear_changes();
2332 vector<Command*> cmds;
2333 orig_tav->playlist()->rdiff (cmds);
2334 _editor->session()->add_commands (cmds);
2336 if (prev_tav && prev_tav != orig_tav) {
2337 prev_tav->playlist()->clear_changes();
2338 vector<Command*> cmds;
2339 prev_tav->playlist()->rdiff (cmds);
2340 _editor->session()->add_commands (cmds);
2343 // selection spanned multiple tracks - all will need adding to undo record
2345 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2346 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2348 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2349 (*pi)->clear_changes();
2350 vector<Command*> cmds;
2351 (*pi)->rdiff (cmds);
2352 _editor->session()->add_commands (cmds);
2356 // other modified playlists are added to undo by RegionMoveDrag::finished()
2357 RegionMoveDrag::finished (event, movement_occurred);
2358 _editor->commit_reversible_command();
2362 RegionRippleDrag::aborted (bool movement_occurred)
2364 RegionMoveDrag::aborted (movement_occurred);
2369 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2371 _view (dynamic_cast<MidiTimeAxisView*> (v))
2373 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2379 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2382 _editor->begin_reversible_command (_("create region"));
2383 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2384 _view->playlist()->freeze ();
2387 framepos_t const f = adjusted_current_frame (event);
2388 if (f < grab_frame()) {
2389 _region->set_initial_position (f);
2392 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2393 so that if this region is duplicated, its duplicate starts on
2394 a snap point rather than 1 frame after a snap point. Otherwise things get
2395 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2396 place snapped notes at the start of the region.
2399 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2400 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2406 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2408 if (!movement_occurred) {
2409 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2411 _view->playlist()->thaw ();
2412 _editor->commit_reversible_command();
2417 RegionCreateDrag::aborted (bool)
2420 _view->playlist()->thaw ();
2426 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2431 , _was_selected (false)
2434 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2438 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2440 Gdk::Cursor* cursor;
2441 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2443 float x_fraction = cnote->mouse_x_fraction ();
2445 if (x_fraction > 0.0 && x_fraction < 0.25) {
2446 cursor = _editor->cursors()->left_side_trim;
2449 cursor = _editor->cursors()->right_side_trim;
2453 Drag::start_grab (event, cursor);
2455 region = &cnote->region_view();
2458 temp = region->snap_to_pixel (cnote->x0 (), true);
2459 _snap_delta = temp - cnote->x0 ();
2463 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2468 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2469 if (ms.size() > 1) {
2470 /* has to be relative, may make no sense otherwise */
2474 if (!(_was_selected = cnote->selected())) {
2476 /* tertiary-click means extend selection - we'll do that on button release,
2477 so don't add it here, because otherwise we make it hard to figure
2478 out the "extend-to" range.
2481 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2484 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2487 region->note_selected (cnote, true);
2489 _editor->get_selection().clear_points();
2490 region->unique_select (cnote);
2497 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2499 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2501 _editor->begin_reversible_command (_("resize notes"));
2503 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2504 MidiRegionSelection::iterator next;
2507 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2509 mrv->begin_resizing (at_front);
2515 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2516 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2518 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2522 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2524 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2525 if (_editor->snap_mode () != SnapOff) {
2529 if (_editor->snap_mode () == SnapOff) {
2531 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2532 if (apply_snap_delta) {
2538 if (apply_snap_delta) {
2542 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2548 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2550 if (!movement_occurred) {
2551 /* no motion - select note */
2552 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2553 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2554 _editor->current_mouse_mode() == Editing::MouseDraw) {
2556 bool changed = false;
2558 if (_was_selected) {
2559 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2561 region->note_deselected (cnote);
2564 _editor->get_selection().clear_points();
2565 region->unique_select (cnote);
2569 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2570 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2572 if (!extend && !add && region->selection_size() > 1) {
2573 _editor->get_selection().clear_points();
2574 region->unique_select (cnote);
2576 } else if (extend) {
2577 region->note_selected (cnote, true, true);
2580 /* it was added during button press */
2586 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2587 _editor->commit_reversible_selection_op();
2594 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2595 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2596 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2598 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2601 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2603 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2604 if (_editor->snap_mode () != SnapOff) {
2608 if (_editor->snap_mode () == SnapOff) {
2610 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2611 if (apply_snap_delta) {
2617 if (apply_snap_delta) {
2621 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2625 _editor->commit_reversible_command ();
2629 NoteResizeDrag::aborted (bool)
2631 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2632 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2633 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2635 mrv->abort_resizing ();
2640 AVDraggingView::AVDraggingView (RegionView* v)
2643 initial_position = v->region()->position ();
2646 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2649 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2652 TrackViewList empty;
2654 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2655 std::list<RegionView*> views = rs.by_layer();
2658 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2659 RegionView* rv = (*i);
2660 if (!rv->region()->video_locked()) {
2663 if (rv->region()->locked()) {
2666 _views.push_back (AVDraggingView (rv));
2671 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2673 Drag::start_grab (event);
2674 if (_editor->session() == 0) {
2678 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2684 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2688 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2689 _max_backwards_drag = (
2690 ARDOUR_UI::instance()->video_timeline->get_duration()
2691 + ARDOUR_UI::instance()->video_timeline->get_offset()
2692 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2695 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2696 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2697 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2700 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2703 Timecode::Time timecode;
2704 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2705 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);
2706 show_verbose_cursor_text (buf);
2710 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2712 if (_editor->session() == 0) {
2715 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2719 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2723 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2724 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2726 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2727 dt = - _max_backwards_drag;
2730 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2731 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2733 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2734 RegionView* rv = i->view;
2735 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2738 rv->region()->clear_changes ();
2739 rv->region()->suspend_property_changes();
2741 rv->region()->set_position(i->initial_position + dt);
2742 rv->region_changed(ARDOUR::Properties::position);
2745 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2746 Timecode::Time timecode;
2747 Timecode::Time timediff;
2749 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2750 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2751 snprintf (buf, sizeof (buf),
2752 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2753 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2754 , _("Video Start:"),
2755 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2757 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2759 show_verbose_cursor_text (buf);
2763 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2765 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2772 if (!movement_occurred || ! _editor->session()) {
2776 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2778 _editor->begin_reversible_command (_("Move Video"));
2780 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2781 ARDOUR_UI::instance()->video_timeline->save_undo();
2782 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2783 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2785 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2786 i->view->drag_end();
2787 i->view->region()->resume_property_changes ();
2789 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2792 _editor->session()->maybe_update_session_range(
2793 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2794 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2798 _editor->commit_reversible_command ();
2802 VideoTimeLineDrag::aborted (bool)
2804 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2807 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2808 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2810 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2811 i->view->region()->resume_property_changes ();
2812 i->view->region()->set_position(i->initial_position);
2816 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2817 : RegionDrag (e, i, p, v)
2818 , _operation (StartTrim)
2819 , _preserve_fade_anchor (preserve_fade_anchor)
2820 , _jump_position_when_done (false)
2822 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2826 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2829 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2830 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2832 if (tv && tv->is_track()) {
2833 speed = tv->track()->speed();
2836 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2837 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2838 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2840 framepos_t const pf = adjusted_current_frame (event);
2841 setup_snap_delta (region_start);
2843 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2844 /* Move the contents of the region around without changing the region bounds */
2845 _operation = ContentsTrim;
2846 Drag::start_grab (event, _editor->cursors()->trimmer);
2848 /* These will get overridden for a point trim.*/
2849 if (pf < (region_start + region_length/2)) {
2850 /* closer to front */
2851 _operation = StartTrim;
2852 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2853 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2855 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2859 _operation = EndTrim;
2860 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2861 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2863 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2867 /* jump trim disabled for now
2868 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2869 _jump_position_when_done = true;
2873 switch (_operation) {
2875 show_verbose_cursor_time (region_start);
2878 show_verbose_cursor_duration (region_start, region_end);
2881 show_verbose_cursor_time (pf);
2885 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2886 i->view->region()->suspend_property_changes ();
2891 TrimDrag::motion (GdkEvent* event, bool first_move)
2893 RegionView* rv = _primary;
2896 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2897 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2898 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2899 frameoffset_t frame_delta = 0;
2901 if (tv && tv->is_track()) {
2902 speed = tv->track()->speed();
2904 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2905 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2911 switch (_operation) {
2913 trim_type = "Region start trim";
2916 trim_type = "Region end trim";
2919 trim_type = "Region content trim";
2926 _editor->begin_reversible_command (trim_type);
2928 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2929 RegionView* rv = i->view;
2930 rv->region()->playlist()->clear_owned_changes ();
2932 if (_operation == StartTrim) {
2933 rv->trim_front_starting ();
2936 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2939 arv->temporarily_hide_envelope ();
2943 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2944 insert_result = _editor->motion_frozen_playlists.insert (pl);
2946 if (insert_result.second) {
2952 bool non_overlap_trim = false;
2954 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2955 non_overlap_trim = true;
2958 /* contstrain trim to fade length */
2959 if (_preserve_fade_anchor) {
2960 switch (_operation) {
2962 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2963 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2965 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2966 if (ar->locked()) continue;
2967 framecnt_t len = ar->fade_in()->back()->when;
2968 if (len < dt) dt = min(dt, len);
2972 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2973 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2975 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2976 if (ar->locked()) continue;
2977 framecnt_t len = ar->fade_out()->back()->when;
2978 if (len < -dt) dt = max(dt, -len);
2986 int32_t divisions = current_music_divisor (adj_frame, event->button.state);
2988 switch (_operation) {
2990 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2991 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2994 if (changed && _preserve_fade_anchor) {
2995 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2997 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2998 framecnt_t len = ar->fade_in()->back()->when;
2999 framecnt_t diff = ar->first_frame() - i->initial_position;
3000 framepos_t new_length = len - diff;
3001 i->anchored_fade_length = min (ar->length(), new_length);
3002 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
3003 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3010 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3011 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, divisions);
3012 if (changed && _preserve_fade_anchor) {
3013 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3015 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3016 framecnt_t len = ar->fade_out()->back()->when;
3017 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3018 framepos_t new_length = len + diff;
3019 i->anchored_fade_length = min (ar->length(), new_length);
3020 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3021 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3029 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3031 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3032 i->view->move_contents (frame_delta);
3038 switch (_operation) {
3040 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3043 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3046 // show_verbose_cursor_time (frame_delta);
3052 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3054 if (movement_occurred) {
3055 motion (event, false);
3057 if (_operation == StartTrim) {
3058 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3060 /* This must happen before the region's StatefulDiffCommand is created, as it may
3061 `correct' (ahem) the region's _start from being negative to being zero. It
3062 needs to be zero in the undo record.
3064 i->view->trim_front_ending ();
3066 if (_preserve_fade_anchor) {
3067 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3069 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3070 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3071 ar->set_fade_in_length(i->anchored_fade_length);
3072 ar->set_fade_in_active(true);
3075 if (_jump_position_when_done) {
3076 i->view->region()->set_position (i->initial_position);
3079 } else if (_operation == EndTrim) {
3080 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3081 if (_preserve_fade_anchor) {
3082 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3084 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3085 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3086 ar->set_fade_out_length(i->anchored_fade_length);
3087 ar->set_fade_out_active(true);
3090 if (_jump_position_when_done) {
3091 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3096 if (!_views.empty()) {
3097 if (_operation == StartTrim) {
3098 _editor->maybe_locate_with_edit_preroll(
3099 _views.begin()->view->region()->position());
3101 if (_operation == EndTrim) {
3102 _editor->maybe_locate_with_edit_preroll(
3103 _views.begin()->view->region()->position() +
3104 _views.begin()->view->region()->length());
3108 if (!_editor->selection->selected (_primary)) {
3109 _primary->thaw_after_trim ();
3112 set<boost::shared_ptr<Playlist> > diffed_playlists;
3114 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3115 i->view->thaw_after_trim ();
3116 i->view->enable_display (true);
3118 /* Trimming one region may affect others on the playlist, so we need
3119 to get undo Commands from the whole playlist rather than just the
3120 region. Use diffed_playlists to make sure we don't diff a given
3121 playlist more than once.
3123 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3124 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3125 vector<Command*> cmds;
3127 _editor->session()->add_commands (cmds);
3128 diffed_playlists.insert (p);
3133 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3137 _editor->motion_frozen_playlists.clear ();
3138 _editor->commit_reversible_command();
3141 /* no mouse movement */
3142 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3143 _editor->point_trim (event, adjusted_current_frame (event));
3147 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3148 i->view->region()->resume_property_changes ();
3153 TrimDrag::aborted (bool movement_occurred)
3155 /* Our motion method is changing model state, so use the Undo system
3156 to cancel. Perhaps not ideal, as this will leave an Undo point
3157 behind which may be slightly odd from the user's point of view.
3161 finished (&ev, true);
3163 if (movement_occurred) {
3164 _editor->session()->undo (1);
3167 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3168 i->view->region()->resume_property_changes ();
3173 TrimDrag::setup_pointer_frame_offset ()
3175 list<DraggingView>::iterator i = _views.begin ();
3176 while (i != _views.end() && i->view != _primary) {
3180 if (i == _views.end()) {
3184 switch (_operation) {
3186 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3189 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3196 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3199 , _old_snap_type (e->snap_type())
3200 , _old_snap_mode (e->snap_mode())
3203 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3204 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3206 _real_section = &_marker->meter();
3211 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3213 Drag::start_grab (event, cursor);
3214 show_verbose_cursor_time (adjusted_current_frame(event));
3218 MeterMarkerDrag::setup_pointer_frame_offset ()
3220 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3224 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3227 // create a dummy marker to catch events, then hide it.
3230 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3232 _marker = new MeterMarker (
3234 *_editor->meter_group,
3235 UIConfiguration::instance().color ("meter marker"),
3237 *new MeterSection (_marker->meter())
3240 /* use the new marker for the grab */
3241 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3244 TempoMap& map (_editor->session()->tempo_map());
3245 /* get current state */
3246 before_state = &map.get_state();
3249 _editor->begin_reversible_command (_("move meter mark"));
3251 _editor->begin_reversible_command (_("copy meter mark"));
3253 Timecode::BBT_Time bbt = _real_section->bbt();
3255 /* we can't add a meter where one currently exists */
3256 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3261 const double beat = map.beat_at_bbt (bbt);
3262 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3263 , beat, bbt, _real_section->position_lock_style());
3264 if (!_real_section) {
3270 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3271 if (_real_section->position_lock_style() != AudioTime) {
3272 _editor->set_snap_to (SnapToBar);
3273 _editor->set_snap_mode (SnapNormal);
3277 framepos_t pf = adjusted_current_frame (event);
3279 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3280 /* never snap to music for audio locked */
3281 pf = adjusted_current_frame (event, false);
3284 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3286 /* fake marker meeds to stay under the mouse, unlike the real one. */
3287 _marker->set_position (adjusted_current_frame (event, false));
3289 show_verbose_cursor_time (_real_section->frame());
3293 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3295 if (!movement_occurred) {
3296 if (was_double_click()) {
3297 _editor->edit_meter_marker (*_marker);
3302 /* reinstate old snap setting */
3303 _editor->set_snap_to (_old_snap_type);
3304 _editor->set_snap_mode (_old_snap_mode);
3306 TempoMap& map (_editor->session()->tempo_map());
3308 XMLNode &after = map.get_state();
3309 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3310 _editor->commit_reversible_command ();
3312 // delete the dummy marker we used for visual representation while moving.
3313 // a new visual marker will show up automatically.
3318 MeterMarkerDrag::aborted (bool moved)
3320 _marker->set_position (_marker->meter().frame ());
3322 /* reinstate old snap setting */
3323 _editor->set_snap_to (_old_snap_type);
3324 _editor->set_snap_mode (_old_snap_mode);
3326 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3327 // delete the dummy marker we used for visual representation while moving.
3328 // a new visual marker will show up automatically.
3333 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3339 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3341 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3342 _real_section = &_marker->tempo();
3343 _movable = !_real_section->initial();
3344 _grab_bpm = _real_section->note_types_per_minute();
3349 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3351 Drag::start_grab (event, cursor);
3352 if (!_real_section->active()) {
3353 show_verbose_cursor_text (_("inactive"));
3355 show_verbose_cursor_time (adjusted_current_frame (event));
3360 TempoMarkerDrag::setup_pointer_frame_offset ()
3362 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3366 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3368 if (!_real_section->active()) {
3374 // mvc drag - create a dummy marker to catch events, hide it.
3377 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3379 TempoSection section (_marker->tempo());
3381 _marker = new TempoMarker (
3383 *_editor->tempo_group,
3384 UIConfiguration::instance().color ("tempo marker"),
3386 *new TempoSection (_marker->tempo())
3389 /* use the new marker for the grab */
3390 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3393 TempoMap& map (_editor->session()->tempo_map());
3394 /* get current state */
3395 before_state = &map.get_state();
3398 _editor->begin_reversible_command (_("move tempo mark"));
3401 const Tempo tempo (_marker->tempo());
3402 const framepos_t frame = adjusted_current_frame (event) + 1;
3403 const TempoSection::Type type = _real_section->type();
3405 _editor->begin_reversible_command (_("copy tempo mark"));
3407 if (_real_section->position_lock_style() == MusicTime) {
3408 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3409 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3411 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3414 if (!_real_section) {
3422 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3423 /* use vertical movement to alter tempo .. should be log */
3424 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3426 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3428 show_verbose_cursor_text (strs.str());
3430 } else if (_movable && !_real_section->locked_to_meter()) {
3433 if (_editor->snap_musical()) {
3434 /* we can't snap to a grid that we are about to move.
3435 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3437 pf = adjusted_current_frame (event, false);
3439 pf = adjusted_current_frame (event);
3442 TempoMap& map (_editor->session()->tempo_map());
3444 /* snap to beat is 1, snap to bar is -1 (sorry) */
3445 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3447 map.gui_move_tempo (_real_section, pf, sub_num);
3449 show_verbose_cursor_time (_real_section->frame());
3451 _marker->set_position (adjusted_current_frame (event, false));
3455 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3457 if (!_real_section->active()) {
3460 if (!movement_occurred) {
3461 if (was_double_click()) {
3462 _editor->edit_tempo_marker (*_marker);
3467 TempoMap& map (_editor->session()->tempo_map());
3469 XMLNode &after = map.get_state();
3470 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3471 _editor->commit_reversible_command ();
3473 // delete the dummy marker we used for visual representation while moving.
3474 // a new visual marker will show up automatically.
3479 TempoMarkerDrag::aborted (bool moved)
3481 _marker->set_position (_marker->tempo().frame());
3483 TempoMap& map (_editor->session()->tempo_map());
3484 map.set_state (*before_state, Stateful::current_state_version);
3485 // delete the dummy (hidden) marker we used for events while moving.
3490 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3496 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3501 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3503 Drag::start_grab (event, cursor);
3504 TempoMap& map (_editor->session()->tempo_map());
3505 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3508 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3509 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3510 show_verbose_cursor_text (sstr.str());
3511 finished (event, false);
3515 BBTRulerDrag::setup_pointer_frame_offset ()
3517 TempoMap& map (_editor->session()->tempo_map());
3518 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3519 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3522 if (divisions > 0) {
3523 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3525 /* while it makes some sense for the user to determine the division to 'grab',
3526 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3527 and the result over steep tempo curves. Use sixteenths.
3529 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3532 _grab_qn = map.quarter_note_at_beat (beat);
3534 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3539 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3541 TempoMap& map (_editor->session()->tempo_map());
3544 /* get current state */
3545 before_state = &map.get_state();
3546 _editor->begin_reversible_command (_("dilate tempo"));
3551 if (_editor->snap_musical()) {
3552 pf = adjusted_current_frame (event, false);
3554 pf = adjusted_current_frame (event);
3557 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3558 /* adjust previous tempo to match pointer frame */
3559 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3562 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3563 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3564 show_verbose_cursor_text (sstr.str());
3568 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3570 if (!movement_occurred) {
3574 TempoMap& map (_editor->session()->tempo_map());
3576 XMLNode &after = map.get_state();
3577 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3578 _editor->commit_reversible_command ();
3582 BBTRulerDrag::aborted (bool moved)
3585 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3590 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3591 : Drag (e, &c.track_canvas_item(), false)
3596 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3599 /** Do all the things we do when dragging the playhead to make it look as though
3600 * we have located, without actually doing the locate (because that would cause
3601 * the diskstream buffers to be refilled, which is too slow).
3604 CursorDrag::fake_locate (framepos_t t)
3606 if (_editor->session () == 0) {
3610 _editor->playhead_cursor->set_position (t);
3612 Session* s = _editor->session ();
3613 if (s->timecode_transmission_suspended ()) {
3614 framepos_t const f = _editor->playhead_cursor->current_frame ();
3615 /* This is asynchronous so it will be sent "now"
3617 s->send_mmc_locate (f);
3618 /* These are synchronous and will be sent during the next
3621 s->queue_full_time_code ();
3622 s->queue_song_position_pointer ();
3625 show_verbose_cursor_time (t);
3626 _editor->UpdateAllTransportClocks (t);
3630 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3632 Drag::start_grab (event, c);
3633 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3635 _grab_zoom = _editor->samples_per_pixel;
3637 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3639 _editor->snap_to_with_modifier (where, event);
3641 _editor->_dragging_playhead = true;
3643 Session* s = _editor->session ();
3645 /* grab the track canvas item as well */
3647 _cursor.track_canvas_item().grab();
3650 if (_was_rolling && _stop) {
3654 if (s->is_auditioning()) {
3655 s->cancel_audition ();
3659 if (AudioEngine::instance()->connected()) {
3661 /* do this only if we're the engine is connected
3662 * because otherwise this request will never be
3663 * serviced and we'll busy wait forever. likewise,
3664 * notice if we are disconnected while waiting for the
3665 * request to be serviced.
3668 s->request_suspend_timecode_transmission ();
3669 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3670 /* twiddle our thumbs */
3675 fake_locate (where - snap_delta (event->button.state));
3679 CursorDrag::motion (GdkEvent* event, bool)
3681 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3682 _editor->snap_to_with_modifier (where, event);
3683 if (where != last_pointer_frame()) {
3684 fake_locate (where - snap_delta (event->button.state));
3689 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3691 _editor->_dragging_playhead = false;
3693 _cursor.track_canvas_item().ungrab();
3695 if (!movement_occurred && _stop) {
3699 motion (event, false);
3701 Session* s = _editor->session ();
3703 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3704 _editor->_pending_locate_request = true;
3705 s->request_resume_timecode_transmission ();
3710 CursorDrag::aborted (bool)
3712 _cursor.track_canvas_item().ungrab();
3714 if (_editor->_dragging_playhead) {
3715 _editor->session()->request_resume_timecode_transmission ();
3716 _editor->_dragging_playhead = false;
3719 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3722 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3723 : RegionDrag (e, i, p, v)
3725 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3729 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3731 Drag::start_grab (event, cursor);
3733 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3734 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3735 setup_snap_delta (r->position ());
3737 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3741 FadeInDrag::setup_pointer_frame_offset ()
3743 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3744 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3745 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3749 FadeInDrag::motion (GdkEvent* event, bool)
3751 framecnt_t fade_length;
3753 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3754 _editor->snap_to_with_modifier (pos, event);
3755 pos -= snap_delta (event->button.state);
3757 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3759 if (pos < (region->position() + 64)) {
3760 fade_length = 64; // this should be a minimum defined somewhere
3761 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3762 fade_length = region->length() - region->fade_out()->back()->when - 1;
3764 fade_length = pos - region->position();
3767 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3769 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3775 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3778 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3782 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3784 if (!movement_occurred) {
3788 framecnt_t fade_length;
3789 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3790 _editor->snap_to_with_modifier (pos, event);
3791 pos -= snap_delta (event->button.state);
3793 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3795 if (pos < (region->position() + 64)) {
3796 fade_length = 64; // this should be a minimum defined somewhere
3797 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3798 fade_length = region->length() - region->fade_out()->back()->when - 1;
3800 fade_length = pos - region->position();
3803 bool in_command = false;
3805 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3807 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3813 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3814 XMLNode &before = alist->get_state();
3816 tmp->audio_region()->set_fade_in_length (fade_length);
3817 tmp->audio_region()->set_fade_in_active (true);
3820 _editor->begin_reversible_command (_("change fade in length"));
3823 XMLNode &after = alist->get_state();
3824 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3828 _editor->commit_reversible_command ();
3833 FadeInDrag::aborted (bool)
3835 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3836 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3842 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3846 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3847 : RegionDrag (e, i, p, v)
3849 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3853 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3855 Drag::start_grab (event, cursor);
3857 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3858 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3859 setup_snap_delta (r->last_frame ());
3861 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3865 FadeOutDrag::setup_pointer_frame_offset ()
3867 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3868 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3869 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3873 FadeOutDrag::motion (GdkEvent* event, bool)
3875 framecnt_t fade_length;
3877 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3878 _editor->snap_to_with_modifier (pos, event);
3879 pos -= snap_delta (event->button.state);
3881 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3883 if (pos > (region->last_frame() - 64)) {
3884 fade_length = 64; // this should really be a minimum fade defined somewhere
3885 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3886 fade_length = region->length() - region->fade_in()->back()->when - 1;
3888 fade_length = region->last_frame() - pos;
3891 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3893 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3899 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3902 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3906 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3908 if (!movement_occurred) {
3912 framecnt_t fade_length;
3914 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3915 _editor->snap_to_with_modifier (pos, event);
3916 pos -= snap_delta (event->button.state);
3918 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3920 if (pos > (region->last_frame() - 64)) {
3921 fade_length = 64; // this should really be a minimum fade defined somewhere
3922 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3923 fade_length = region->length() - region->fade_in()->back()->when - 1;
3925 fade_length = region->last_frame() - pos;
3928 bool in_command = false;
3930 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3932 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3938 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3939 XMLNode &before = alist->get_state();
3941 tmp->audio_region()->set_fade_out_length (fade_length);
3942 tmp->audio_region()->set_fade_out_active (true);
3945 _editor->begin_reversible_command (_("change fade out length"));
3948 XMLNode &after = alist->get_state();
3949 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3953 _editor->commit_reversible_command ();
3958 FadeOutDrag::aborted (bool)
3960 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3961 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3967 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3971 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3973 , _selection_changed (false)
3975 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3976 Gtk::Window* toplevel = _editor->current_toplevel();
3977 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3981 _points.push_back (ArdourCanvas::Duple (0, 0));
3983 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3986 MarkerDrag::~MarkerDrag ()
3988 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3993 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3995 location = new Location (*l);
3996 markers.push_back (m);
4001 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4003 Drag::start_grab (event, cursor);
4007 Location *location = _editor->find_location_from_marker (_marker, is_start);
4008 _editor->_dragging_edit_point = true;
4010 update_item (location);
4012 // _drag_line->show();
4013 // _line->raise_to_top();
4016 show_verbose_cursor_time (location->start());
4018 show_verbose_cursor_time (location->end());
4020 setup_snap_delta (is_start ? location->start() : location->end());
4022 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4025 case Selection::Toggle:
4026 /* we toggle on the button release */
4028 case Selection::Set:
4029 if (!_editor->selection->selected (_marker)) {
4030 _editor->selection->set (_marker);
4031 _selection_changed = true;
4034 case Selection::Extend:
4036 Locations::LocationList ll;
4037 list<ArdourMarker*> to_add;
4039 _editor->selection->markers.range (s, e);
4040 s = min (_marker->position(), s);
4041 e = max (_marker->position(), e);
4044 if (e < max_framepos) {
4047 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4048 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4049 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4052 to_add.push_back (lm->start);
4055 to_add.push_back (lm->end);
4059 if (!to_add.empty()) {
4060 _editor->selection->add (to_add);
4061 _selection_changed = true;
4065 case Selection::Add:
4066 _editor->selection->add (_marker);
4067 _selection_changed = true;
4072 /* Set up copies for us to manipulate during the drag
4075 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4077 Location* l = _editor->find_location_from_marker (*i, is_start);
4084 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4086 /* range: check that the other end of the range isn't
4089 CopiedLocationInfo::iterator x;
4090 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4091 if (*(*x).location == *l) {
4095 if (x == _copied_locations.end()) {
4096 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4098 (*x).markers.push_back (*i);
4099 (*x).move_both = true;
4107 MarkerDrag::setup_pointer_frame_offset ()
4110 Location *location = _editor->find_location_from_marker (_marker, is_start);
4111 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4115 MarkerDrag::motion (GdkEvent* event, bool)
4117 framecnt_t f_delta = 0;
4119 bool move_both = false;
4120 Location *real_location;
4121 Location *copy_location = 0;
4122 framecnt_t const sd = snap_delta (event->button.state);
4124 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4125 framepos_t next = newframe;
4127 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4131 CopiedLocationInfo::iterator x;
4133 /* find the marker we're dragging, and compute the delta */
4135 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4137 copy_location = (*x).location;
4139 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4141 /* this marker is represented by this
4142 * CopiedLocationMarkerInfo
4145 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4150 if (real_location->is_mark()) {
4151 f_delta = newframe - copy_location->start();
4155 switch (_marker->type()) {
4156 case ArdourMarker::SessionStart:
4157 case ArdourMarker::RangeStart:
4158 case ArdourMarker::LoopStart:
4159 case ArdourMarker::PunchIn:
4160 f_delta = newframe - copy_location->start();
4163 case ArdourMarker::SessionEnd:
4164 case ArdourMarker::RangeEnd:
4165 case ArdourMarker::LoopEnd:
4166 case ArdourMarker::PunchOut:
4167 f_delta = newframe - copy_location->end();
4170 /* what kind of marker is this ? */
4179 if (x == _copied_locations.end()) {
4180 /* hmm, impossible - we didn't find the dragged marker */
4184 /* now move them all */
4186 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4188 copy_location = x->location;
4190 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4194 if (real_location->locked()) {
4198 if (copy_location->is_mark()) {
4202 copy_location->set_start (copy_location->start() + f_delta);
4206 framepos_t new_start = copy_location->start() + f_delta;
4207 framepos_t new_end = copy_location->end() + f_delta;
4209 if (is_start) { // start-of-range marker
4211 if (move_both || (*x).move_both) {
4212 copy_location->set_start (new_start);
4213 copy_location->set_end (new_end);
4214 } else if (new_start < copy_location->end()) {
4215 copy_location->set_start (new_start);
4216 } else if (newframe > 0) {
4217 //_editor->snap_to (next, RoundUpAlways, true);
4218 copy_location->set_end (next);
4219 copy_location->set_start (newframe);
4222 } else { // end marker
4224 if (move_both || (*x).move_both) {
4225 copy_location->set_end (new_end);
4226 copy_location->set_start (new_start);
4227 } else if (new_end > copy_location->start()) {
4228 copy_location->set_end (new_end);
4229 } else if (newframe > 0) {
4230 //_editor->snap_to (next, RoundDownAlways, true);
4231 copy_location->set_start (next);
4232 copy_location->set_end (newframe);
4237 update_item (copy_location);
4239 /* now lookup the actual GUI items used to display this
4240 * location and move them to wherever the copy of the location
4241 * is now. This means that the logic in ARDOUR::Location is
4242 * still enforced, even though we are not (yet) modifying
4243 * the real Location itself.
4246 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4249 lm->set_position (copy_location->start(), copy_location->end());
4254 assert (!_copied_locations.empty());
4256 show_verbose_cursor_time (newframe);
4260 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4262 if (!movement_occurred) {
4264 if (was_double_click()) {
4265 _editor->rename_marker (_marker);
4269 /* just a click, do nothing but finish
4270 off the selection process
4273 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4275 case Selection::Set:
4276 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4277 _editor->selection->set (_marker);
4278 _selection_changed = true;
4282 case Selection::Toggle:
4283 /* we toggle on the button release, click only */
4284 _editor->selection->toggle (_marker);
4285 _selection_changed = true;
4289 case Selection::Extend:
4290 case Selection::Add:
4294 if (_selection_changed) {
4295 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4296 _editor->commit_reversible_selection_op();
4302 _editor->_dragging_edit_point = false;
4304 XMLNode &before = _editor->session()->locations()->get_state();
4305 bool in_command = false;
4307 MarkerSelection::iterator i;
4308 CopiedLocationInfo::iterator x;
4311 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4312 x != _copied_locations.end() && i != _editor->selection->markers.end();
4315 Location * location = _editor->find_location_from_marker (*i, is_start);
4319 if (location->locked()) {
4323 _editor->begin_reversible_command ( _("move marker") );
4326 if (location->is_mark()) {
4327 location->set_start (((*x).location)->start());
4329 location->set (((*x).location)->start(), ((*x).location)->end());
4332 if (location->is_session_range()) {
4333 _editor->session()->set_end_is_free (false);
4339 XMLNode &after = _editor->session()->locations()->get_state();
4340 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4341 _editor->commit_reversible_command ();
4346 MarkerDrag::aborted (bool movement_occurred)
4348 if (!movement_occurred) {
4352 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4354 /* move all markers to their original location */
4357 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4360 Location * location = _editor->find_location_from_marker (*m, is_start);
4363 (*m)->set_position (is_start ? location->start() : location->end());
4370 MarkerDrag::update_item (Location*)
4375 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4377 , _fixed_grab_x (0.0)
4378 , _fixed_grab_y (0.0)
4379 , _cumulative_x_drag (0.0)
4380 , _cumulative_y_drag (0.0)
4384 if (_zero_gain_fraction < 0.0) {
4385 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4388 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4390 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4396 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4398 Drag::start_grab (event, _editor->cursors()->fader);
4400 // start the grab at the center of the control point so
4401 // the point doesn't 'jump' to the mouse after the first drag
4402 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4403 _fixed_grab_y = _point->get_y();
4405 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4406 setup_snap_delta (pos);
4408 float const fraction = 1 - (_point->get_y() / _point->line().height());
4409 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4411 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4413 if (!_point->can_slide ()) {
4414 _x_constrained = true;
4419 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4421 double dx = _drags->current_pointer_x() - last_pointer_x();
4422 double dy = current_pointer_y() - last_pointer_y();
4423 bool need_snap = true;
4425 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4431 /* coordinate in pixels relative to the start of the region (for region-based automation)
4432 or track (for track-based automation) */
4433 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4434 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4436 // calculate zero crossing point. back off by .01 to stay on the
4437 // positive side of zero
4438 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4440 if (_x_constrained) {
4443 if (_y_constrained) {
4447 _cumulative_x_drag = cx - _fixed_grab_x;
4448 _cumulative_y_drag = cy - _fixed_grab_y;
4452 cy = min ((double) _point->line().height(), cy);
4454 // make sure we hit zero when passing through
4455 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4459 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4460 if (!_x_constrained && need_snap) {
4461 _editor->snap_to_with_modifier (cx_frames, event);
4464 cx_frames -= snap_delta (event->button.state);
4465 cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4467 float const fraction = 1.0 - (cy / _point->line().height());
4470 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4471 _editor->begin_reversible_command (_("automation event move"));
4472 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4474 pair<double, float> result;
4475 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4477 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4481 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4483 if (!movement_occurred) {
4486 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4487 _editor->reset_point_selection ();
4491 _point->line().end_drag (_pushing, _final_index);
4492 _editor->commit_reversible_command ();
4497 ControlPointDrag::aborted (bool)
4499 _point->line().reset ();
4503 ControlPointDrag::active (Editing::MouseMode m)
4505 if (m == Editing::MouseDraw) {
4506 /* always active in mouse draw */
4510 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4511 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4514 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4517 , _fixed_grab_x (0.0)
4518 , _fixed_grab_y (0.0)
4519 , _cumulative_y_drag (0)
4523 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4527 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4529 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4532 _item = &_line->grab_item ();
4534 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4535 origin, and ditto for y.
4538 double mx = event->button.x;
4539 double my = event->button.y;
4541 _line->grab_item().canvas_to_item (mx, my);
4543 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4545 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4546 /* no adjacent points */
4550 Drag::start_grab (event, _editor->cursors()->fader);
4552 /* store grab start in item frame */
4553 double const bx = _line->nth (_before)->get_x();
4554 double const ax = _line->nth (_after)->get_x();
4555 double const click_ratio = (ax - mx) / (ax - bx);
4557 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4562 double fraction = 1.0 - (cy / _line->height());
4564 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4568 LineDrag::motion (GdkEvent* event, bool first_move)
4570 double dy = current_pointer_y() - last_pointer_y();
4572 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4576 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4578 _cumulative_y_drag = cy - _fixed_grab_y;
4581 cy = min ((double) _line->height(), cy);
4583 double const fraction = 1.0 - (cy / _line->height());
4587 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4589 _editor->begin_reversible_command (_("automation range move"));
4590 _line->start_drag_line (_before, _after, initial_fraction);
4593 /* we are ignoring x position for this drag, so we can just pass in anything */
4594 pair<double, float> result;
4596 result = _line->drag_motion (0, fraction, true, false, ignored);
4597 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4601 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4603 if (movement_occurred) {
4604 motion (event, false);
4605 _line->end_drag (false, 0);
4606 _editor->commit_reversible_command ();
4608 /* add a new control point on the line */
4610 AutomationTimeAxisView* atv;
4612 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4613 framepos_t where = grab_frame ();
4616 double cy = _fixed_grab_y;
4618 _line->grab_item().item_to_canvas (cx, cy);
4620 atv->add_automation_event (event, where, cy, false);
4621 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4622 AudioRegionView* arv;
4624 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4625 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4632 LineDrag::aborted (bool)
4637 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4641 _region_view_grab_x (0.0),
4642 _cumulative_x_drag (0),
4646 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4650 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4652 Drag::start_grab (event);
4654 _line = reinterpret_cast<Line*> (_item);
4657 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4659 double cx = event->button.x;
4660 double cy = event->button.y;
4662 _item->parent()->canvas_to_item (cx, cy);
4664 /* store grab start in parent frame */
4665 _region_view_grab_x = cx;
4667 _before = *(float*) _item->get_data ("position");
4669 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4671 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4675 FeatureLineDrag::motion (GdkEvent*, bool)
4677 double dx = _drags->current_pointer_x() - last_pointer_x();
4679 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4681 _cumulative_x_drag += dx;
4683 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4692 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4694 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4696 float *pos = new float;
4699 _line->set_data ("position", pos);
4705 FeatureLineDrag::finished (GdkEvent*, bool)
4707 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4708 _arv->update_transient(_before, _before);
4712 FeatureLineDrag::aborted (bool)
4717 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4719 , _vertical_only (false)
4721 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4725 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4727 Drag::start_grab (event);
4728 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4732 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4739 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4741 framepos_t grab = grab_frame ();
4742 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4743 _editor->snap_to_with_modifier (grab, event);
4745 grab = raw_grab_frame ();
4748 /* base start and end on initial click position */
4758 if (current_pointer_y() < grab_y()) {
4759 y1 = current_pointer_y();
4762 y2 = current_pointer_y();
4766 if (start != end || y1 != y2) {
4768 double x1 = _editor->sample_to_pixel (start);
4769 double x2 = _editor->sample_to_pixel (end);
4770 const double min_dimension = 2.0;
4772 if (_vertical_only) {
4773 /* fixed 10 pixel width */
4777 x2 = min (x1 - min_dimension, x2);
4779 x2 = max (x1 + min_dimension, x2);
4784 y2 = min (y1 - min_dimension, y2);
4786 y2 = max (y1 + min_dimension, y2);
4789 /* translate rect into item space and set */
4791 ArdourCanvas::Rect r (x1, y1, x2, y2);
4793 /* this drag is a _trackview_only == true drag, so the y1 and
4794 * y2 (computed using current_pointer_y() and grab_y()) will be
4795 * relative to the top of the trackview group). The
4796 * rubberband rect has the same parent/scroll offset as the
4797 * the trackview group, so we can use the "r" rect directly
4798 * to set the shape of the rubberband.
4801 _editor->rubberband_rect->set (r);
4802 _editor->rubberband_rect->show();
4803 _editor->rubberband_rect->raise_to_top();
4805 show_verbose_cursor_time (pf);
4807 do_select_things (event, true);
4812 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4816 framepos_t grab = grab_frame ();
4817 framepos_t lpf = last_pointer_frame ();
4819 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4820 grab = raw_grab_frame ();
4821 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4835 if (current_pointer_y() < grab_y()) {
4836 y1 = current_pointer_y();
4839 y2 = current_pointer_y();
4843 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4847 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4849 if (movement_occurred) {
4851 motion (event, false);
4852 do_select_things (event, false);
4858 bool do_deselect = true;
4859 MidiTimeAxisView* mtv;
4861 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4863 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4864 /* nothing selected */
4865 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4866 do_deselect = false;
4870 /* do not deselect if Primary or Tertiary (toggle-select or
4871 * extend-select are pressed.
4874 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4875 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4882 _editor->rubberband_rect->hide();
4886 RubberbandSelectDrag::aborted (bool)
4888 _editor->rubberband_rect->hide ();
4891 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4892 : RegionDrag (e, i, p, v)
4894 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4898 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4900 Drag::start_grab (event, cursor);
4902 _editor->get_selection().add (_primary);
4904 framepos_t where = _primary->region()->position();
4905 setup_snap_delta (where);
4907 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4911 TimeFXDrag::motion (GdkEvent* event, bool)
4913 RegionView* rv = _primary;
4914 StreamView* cv = rv->get_time_axis_view().view ();
4916 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4917 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4918 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4919 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4920 _editor->snap_to_with_modifier (pf, event);
4921 pf -= snap_delta (event->button.state);
4923 if (pf > rv->region()->position()) {
4924 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4927 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4931 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4933 /* this may have been a single click, no drag. We still want the dialog
4934 to show up in that case, so that the user can manually edit the
4935 parameters for the timestretch.
4938 float fraction = 1.0;
4940 if (movement_occurred) {
4942 motion (event, false);
4944 _primary->get_time_axis_view().hide_timestretch ();
4946 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4948 if (adjusted_frame_pos < _primary->region()->position()) {
4949 /* backwards drag of the left edge - not usable */
4953 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4955 fraction = (double) newlen / (double) _primary->region()->length();
4957 #ifndef USE_RUBBERBAND
4958 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4959 if (_primary->region()->data_type() == DataType::AUDIO) {
4960 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4965 if (!_editor->get_selection().regions.empty()) {
4966 /* primary will already be included in the selection, and edit
4967 group shared editing will propagate selection across
4968 equivalent regions, so just use the current region
4972 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4973 error << _("An error occurred while executing time stretch operation") << endmsg;
4979 TimeFXDrag::aborted (bool)
4981 _primary->get_time_axis_view().hide_timestretch ();
4984 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4987 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4991 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4993 Drag::start_grab (event);
4997 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4999 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5003 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5005 if (movement_occurred && _editor->session()) {
5006 /* make sure we stop */
5007 _editor->session()->request_transport_speed (0.0);
5012 ScrubDrag::aborted (bool)
5017 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5021 , _time_selection_at_start (!_editor->get_selection().time.empty())
5023 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5025 if (_time_selection_at_start) {
5026 start_at_start = _editor->get_selection().time.start();
5027 end_at_start = _editor->get_selection().time.end_frame();
5032 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5034 if (_editor->session() == 0) {
5038 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5040 switch (_operation) {
5041 case CreateSelection:
5042 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5047 cursor = _editor->cursors()->selector;
5048 Drag::start_grab (event, cursor);
5051 case SelectionStartTrim:
5052 if (_editor->clicked_axisview) {
5053 _editor->clicked_axisview->order_selection_trims (_item, true);
5055 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5058 case SelectionEndTrim:
5059 if (_editor->clicked_axisview) {
5060 _editor->clicked_axisview->order_selection_trims (_item, false);
5062 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5066 Drag::start_grab (event, cursor);
5069 case SelectionExtend:
5070 Drag::start_grab (event, cursor);
5074 if (_operation == SelectionMove) {
5075 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5077 show_verbose_cursor_time (adjusted_current_frame (event));
5082 SelectionDrag::setup_pointer_frame_offset ()
5084 switch (_operation) {
5085 case CreateSelection:
5086 _pointer_frame_offset = 0;
5089 case SelectionStartTrim:
5091 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5094 case SelectionEndTrim:
5095 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5098 case SelectionExtend:
5104 SelectionDrag::motion (GdkEvent* event, bool first_move)
5106 framepos_t start = 0;
5108 framecnt_t length = 0;
5109 framecnt_t distance = 0;
5111 framepos_t const pending_position = adjusted_current_frame (event);
5113 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5117 switch (_operation) {
5118 case CreateSelection:
5120 framepos_t grab = grab_frame ();
5123 grab = adjusted_current_frame (event, false);
5124 if (grab < pending_position) {
5125 _editor->snap_to (grab, RoundDownMaybe);
5127 _editor->snap_to (grab, RoundUpMaybe);
5131 if (pending_position < grab) {
5132 start = pending_position;
5135 end = pending_position;
5139 /* first drag: Either add to the selection
5140 or create a new selection
5147 /* adding to the selection */
5148 _editor->set_selected_track_as_side_effect (Selection::Add);
5149 _editor->clicked_selection = _editor->selection->add (start, end);
5156 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5157 _editor->set_selected_track_as_side_effect (Selection::Set);
5160 _editor->clicked_selection = _editor->selection->set (start, end);
5164 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5165 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5166 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5168 _editor->selection->add (atest);
5172 /* select all tracks within the rectangle that we've marked out so far */
5173 TrackViewList new_selection;
5174 TrackViewList& all_tracks (_editor->track_views);
5176 ArdourCanvas::Coord const top = grab_y();
5177 ArdourCanvas::Coord const bottom = current_pointer_y();
5179 if (top >= 0 && bottom >= 0) {
5181 //first, find the tracks that are covered in the y range selection
5182 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5183 if ((*i)->covered_by_y_range (top, bottom)) {
5184 new_selection.push_back (*i);
5188 //now find any tracks that are GROUPED with the tracks we selected
5189 TrackViewList grouped_add = new_selection;
5190 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5191 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5192 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5193 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5194 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5195 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5196 grouped_add.push_back (*j);
5201 //now compare our list with the current selection, and add or remove as necessary
5202 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5203 TrackViewList tracks_to_add;
5204 TrackViewList tracks_to_remove;
5205 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5206 if ( !_editor->selection->tracks.contains ( *i ) )
5207 tracks_to_add.push_back ( *i );
5208 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5209 if ( !grouped_add.contains ( *i ) )
5210 tracks_to_remove.push_back ( *i );
5211 _editor->selection->add(tracks_to_add);
5212 _editor->selection->remove(tracks_to_remove);
5218 case SelectionStartTrim:
5220 end = _editor->selection->time[_editor->clicked_selection].end;
5222 if (pending_position > end) {
5225 start = pending_position;
5229 case SelectionEndTrim:
5231 start = _editor->selection->time[_editor->clicked_selection].start;
5233 if (pending_position < start) {
5236 end = pending_position;
5243 start = _editor->selection->time[_editor->clicked_selection].start;
5244 end = _editor->selection->time[_editor->clicked_selection].end;
5246 length = end - start;
5247 distance = pending_position - start;
5248 start = pending_position;
5249 _editor->snap_to (start);
5251 end = start + length;
5255 case SelectionExtend:
5260 switch (_operation) {
5262 if (_time_selection_at_start) {
5263 _editor->selection->move_time (distance);
5267 _editor->selection->replace (_editor->clicked_selection, start, end);
5271 if (_operation == SelectionMove) {
5272 show_verbose_cursor_time(start);
5274 show_verbose_cursor_time(pending_position);
5279 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5281 Session* s = _editor->session();
5283 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5284 if (movement_occurred) {
5285 motion (event, false);
5286 /* XXX this is not object-oriented programming at all. ick */
5287 if (_editor->selection->time.consolidate()) {
5288 _editor->selection->TimeChanged ();
5291 /* XXX what if its a music time selection? */
5293 if (s->get_play_range() && s->transport_rolling()) {
5294 s->request_play_range (&_editor->selection->time, true);
5295 } else if (!s->config.get_external_sync()) {
5296 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5297 if (_operation == SelectionEndTrim)
5298 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5300 s->request_locate (_editor->get_selection().time.start());
5304 if (_editor->get_selection().time.length() != 0) {
5305 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5307 s->clear_range_selection ();
5312 /* just a click, no pointer movement.
5315 if (was_double_click()) {
5316 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5317 _editor->temporal_zoom_selection (Both);
5322 if (_operation == SelectionExtend) {
5323 if (_time_selection_at_start) {
5324 framepos_t pos = adjusted_current_frame (event, false);
5325 framepos_t start = min (pos, start_at_start);
5326 framepos_t end = max (pos, end_at_start);
5327 _editor->selection->set (start, end);
5330 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5331 if (_editor->clicked_selection) {
5332 _editor->selection->remove (_editor->clicked_selection);
5335 if (!_editor->clicked_selection) {
5336 _editor->selection->clear_time();
5341 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5342 _editor->selection->set (_editor->clicked_axisview);
5345 if (s && s->get_play_range () && s->transport_rolling()) {
5346 s->request_stop (false, false);
5351 _editor->stop_canvas_autoscroll ();
5352 _editor->clicked_selection = 0;
5353 _editor->commit_reversible_selection_op ();
5357 SelectionDrag::aborted (bool)
5362 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5363 : Drag (e, i, false),
5367 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5369 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5370 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5371 physical_screen_height (_editor->current_toplevel()->get_window())));
5372 _drag_rect->hide ();
5374 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5375 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5378 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5380 /* normal canvas items will be cleaned up when their parent group is deleted. But
5381 this item is created as the child of a long-lived parent group, and so we
5382 need to explicitly delete it.
5388 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5390 if (_editor->session() == 0) {
5394 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5396 if (!_editor->temp_location) {
5397 _editor->temp_location = new Location (*_editor->session());
5400 switch (_operation) {
5401 case CreateSkipMarker:
5402 case CreateRangeMarker:
5403 case CreateTransportMarker:
5404 case CreateCDMarker:
5406 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5411 cursor = _editor->cursors()->selector;
5415 Drag::start_grab (event, cursor);
5417 show_verbose_cursor_time (adjusted_current_frame (event));
5421 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5423 framepos_t start = 0;
5425 ArdourCanvas::Rectangle *crect;
5427 switch (_operation) {
5428 case CreateSkipMarker:
5429 crect = _editor->range_bar_drag_rect;
5431 case CreateRangeMarker:
5432 crect = _editor->range_bar_drag_rect;
5434 case CreateTransportMarker:
5435 crect = _editor->transport_bar_drag_rect;
5437 case CreateCDMarker:
5438 crect = _editor->cd_marker_bar_drag_rect;
5441 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5446 framepos_t const pf = adjusted_current_frame (event);
5448 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5449 framepos_t grab = grab_frame ();
5450 _editor->snap_to (grab);
5452 if (pf < grab_frame()) {
5460 /* first drag: Either add to the selection
5461 or create a new selection.
5466 _editor->temp_location->set (start, end);
5470 update_item (_editor->temp_location);
5472 //_drag_rect->raise_to_top();
5478 _editor->temp_location->set (start, end);
5480 double x1 = _editor->sample_to_pixel (start);
5481 double x2 = _editor->sample_to_pixel (end);
5485 update_item (_editor->temp_location);
5488 show_verbose_cursor_time (pf);
5493 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5495 Location * newloc = 0;
5499 if (movement_occurred) {
5500 motion (event, false);
5503 switch (_operation) {
5504 case CreateSkipMarker:
5505 case CreateRangeMarker:
5506 case CreateCDMarker:
5508 XMLNode &before = _editor->session()->locations()->get_state();
5509 if (_operation == CreateSkipMarker) {
5510 _editor->begin_reversible_command (_("new skip marker"));
5511 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5512 flags = Location::IsRangeMarker | Location::IsSkip;
5513 _editor->range_bar_drag_rect->hide();
5514 } else if (_operation == CreateCDMarker) {
5515 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5516 _editor->begin_reversible_command (_("new CD marker"));
5517 flags = Location::IsRangeMarker | Location::IsCDMarker;
5518 _editor->cd_marker_bar_drag_rect->hide();
5520 _editor->begin_reversible_command (_("new skip marker"));
5521 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5522 flags = Location::IsRangeMarker;
5523 _editor->range_bar_drag_rect->hide();
5525 newloc = new Location (
5526 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5529 _editor->session()->locations()->add (newloc, true);
5530 XMLNode &after = _editor->session()->locations()->get_state();
5531 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5532 _editor->commit_reversible_command ();
5536 case CreateTransportMarker:
5537 // popup menu to pick loop or punch
5538 _editor->new_transport_marker_context_menu (&event->button, _item);
5544 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5546 if (_operation == CreateTransportMarker) {
5548 /* didn't drag, so just locate */
5550 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5552 } else if (_operation == CreateCDMarker) {
5554 /* didn't drag, but mark is already created so do
5557 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5562 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5564 if (end == max_framepos) {
5565 end = _editor->session()->current_end_frame ();
5568 if (start == max_framepos) {
5569 start = _editor->session()->current_start_frame ();
5572 switch (_editor->mouse_mode) {
5574 /* find the two markers on either side and then make the selection from it */
5575 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5579 /* find the two markers on either side of the click and make the range out of it */
5580 _editor->selection->set (start, end);
5589 _editor->stop_canvas_autoscroll ();
5593 RangeMarkerBarDrag::aborted (bool movement_occurred)
5595 if (movement_occurred) {
5596 _drag_rect->hide ();
5601 RangeMarkerBarDrag::update_item (Location* location)
5603 double const x1 = _editor->sample_to_pixel (location->start());
5604 double const x2 = _editor->sample_to_pixel (location->end());
5606 _drag_rect->set_x0 (x1);
5607 _drag_rect->set_x1 (x2);
5610 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5612 , _cumulative_dx (0)
5613 , _cumulative_dy (0)
5614 , _was_selected (false)
5616 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5618 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5620 _region = &_primary->region_view ();
5621 _note_height = _region->midi_stream_view()->note_height ();
5625 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5627 Drag::start_grab (event);
5628 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5630 if (!(_was_selected = _primary->selected())) {
5632 /* tertiary-click means extend selection - we'll do that on button release,
5633 so don't add it here, because otherwise we make it hard to figure
5634 out the "extend-to" range.
5637 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5640 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5643 _region->note_selected (_primary, true);
5645 _editor->get_selection().clear_points();
5646 _region->unique_select (_primary);
5652 /** @return Current total drag x change in frames */
5654 NoteDrag::total_dx (const guint state) const
5656 if (_x_constrained) {
5659 TempoMap& map (_editor->session()->tempo_map());
5662 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5664 /* primary note time */
5665 double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5666 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5668 /* new time of the primary note in session frames */
5669 frameoffset_t st = n + dx + snap_delta (state);
5671 framepos_t const rp = _region->region()->position ();
5673 /* prevent the note being dragged earlier than the region's position */
5676 /* possibly snap and return corresponding delta */
5680 if (ArdourKeyboard::indicates_snap (state)) {
5681 if (_editor->snap_mode () != SnapOff) {
5685 if (_editor->snap_mode () == SnapOff) {
5687 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5688 if (ArdourKeyboard::indicates_snap_delta (state)) {
5696 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5697 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5699 ret = st - n - snap_delta (state);
5704 /** @return Current total drag y change in note number */
5706 NoteDrag::total_dy () const
5708 if (_y_constrained) {
5712 double const y = _region->midi_view()->y_position ();
5713 /* new current note */
5714 uint8_t n = _region->y_to_note (current_pointer_y () - y);
5716 MidiStreamView* msv = _region->midi_stream_view ();
5717 n = max (msv->lowest_note(), n);
5718 n = min (msv->highest_note(), n);
5719 /* and work out delta */
5720 return n - _region->y_to_note (grab_y() - y);
5724 NoteDrag::motion (GdkEvent * event, bool)
5726 /* Total change in x and y since the start of the drag */
5727 frameoffset_t const dx = total_dx (event->button.state);
5728 int8_t const dy = total_dy ();
5730 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5731 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5732 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5735 _cumulative_dx += tdx;
5736 _cumulative_dy += tdy;
5738 int8_t note_delta = total_dy();
5741 _region->move_selection (tdx, tdy, note_delta);
5743 /* the new note value may be the same as the old one, but we
5744 * don't know what that means because the selection may have
5745 * involved more than one note and we might be doing something
5746 * odd with them. so show the note value anyway, always.
5749 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5751 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5757 NoteDrag::finished (GdkEvent* ev, bool moved)
5760 /* no motion - select note */
5762 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5763 _editor->current_mouse_mode() == Editing::MouseDraw) {
5765 bool changed = false;
5767 if (_was_selected) {
5768 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5770 _region->note_deselected (_primary);
5773 _editor->get_selection().clear_points();
5774 _region->unique_select (_primary);
5778 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5779 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5781 if (!extend && !add && _region->selection_size() > 1) {
5782 _editor->get_selection().clear_points();
5783 _region->unique_select (_primary);
5785 } else if (extend) {
5786 _region->note_selected (_primary, true, true);
5789 /* it was added during button press */
5796 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5797 _editor->commit_reversible_selection_op();
5801 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5806 NoteDrag::aborted (bool)
5811 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5812 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5813 : Drag (editor, atv->base_item ())
5815 , _y_origin (atv->y_position())
5816 , _nothing_to_drag (false)
5818 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5819 setup (atv->lines ());
5822 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5823 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5824 : Drag (editor, rv->get_canvas_group ())
5826 , _y_origin (rv->get_time_axis_view().y_position())
5827 , _nothing_to_drag (false)
5830 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5832 list<boost::shared_ptr<AutomationLine> > lines;
5834 AudioRegionView* audio_view;
5835 AutomationRegionView* automation_view;
5836 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5837 lines.push_back (audio_view->get_gain_line ());
5838 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5839 lines.push_back (automation_view->line ());
5842 error << _("Automation range drag created for invalid region type") << endmsg;
5848 /** @param lines AutomationLines to drag.
5849 * @param offset Offset from the session start to the points in the AutomationLines.
5852 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5854 /* find the lines that overlap the ranges being dragged */
5855 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5856 while (i != lines.end ()) {
5857 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5860 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5862 /* check this range against all the AudioRanges that we are using */
5863 list<AudioRange>::const_iterator k = _ranges.begin ();
5864 while (k != _ranges.end()) {
5865 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5871 /* add it to our list if it overlaps at all */
5872 if (k != _ranges.end()) {
5877 _lines.push_back (n);
5883 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5887 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5889 return 1.0 - ((global_y - _y_origin) / line->height());
5893 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5895 const double v = list->eval(x);
5896 return _integral ? rint(v) : v;
5900 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5902 Drag::start_grab (event, cursor);
5904 /* Get line states before we start changing things */
5905 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5906 i->state = &i->line->get_state ();
5907 i->original_fraction = y_fraction (i->line, current_pointer_y());
5910 if (_ranges.empty()) {
5912 /* No selected time ranges: drag all points */
5913 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5914 uint32_t const N = i->line->npoints ();
5915 for (uint32_t j = 0; j < N; ++j) {
5916 i->points.push_back (i->line->nth (j));
5922 if (_nothing_to_drag) {
5928 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5930 if (_nothing_to_drag && !first_move) {
5935 _editor->begin_reversible_command (_("automation range move"));
5937 if (!_ranges.empty()) {
5939 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5941 framecnt_t const half = (i->start + i->end) / 2;
5943 /* find the line that this audio range starts in */
5944 list<Line>::iterator j = _lines.begin();
5945 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5949 if (j != _lines.end()) {
5950 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5952 /* j is the line that this audio range starts in; fade into it;
5953 64 samples length plucked out of thin air.
5956 framepos_t a = i->start + 64;
5961 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5962 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5964 XMLNode &before = the_list->get_state();
5965 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5966 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5968 if (add_p || add_q) {
5969 _editor->session()->add_command (
5970 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5974 /* same thing for the end */
5977 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5981 if (j != _lines.end()) {
5982 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5984 /* j is the line that this audio range starts in; fade out of it;
5985 64 samples length plucked out of thin air.
5988 framepos_t b = i->end - 64;
5993 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5994 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5996 XMLNode &before = the_list->get_state();
5997 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5998 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6000 if (add_p || add_q) {
6001 _editor->session()->add_command (
6002 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6007 _nothing_to_drag = true;
6009 /* Find all the points that should be dragged and put them in the relevant
6010 points lists in the Line structs.
6013 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6015 uint32_t const N = i->line->npoints ();
6016 for (uint32_t j = 0; j < N; ++j) {
6018 /* here's a control point on this line */
6019 ControlPoint* p = i->line->nth (j);
6020 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6022 /* see if it's inside a range */
6023 list<AudioRange>::const_iterator k = _ranges.begin ();
6024 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6028 if (k != _ranges.end()) {
6029 /* dragging this point */
6030 _nothing_to_drag = false;
6031 i->points.push_back (p);
6037 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6038 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6042 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6043 float const f = y_fraction (l->line, current_pointer_y());
6044 /* we are ignoring x position for this drag, so we can just pass in anything */
6045 pair<double, float> result;
6047 result = l->line->drag_motion (0, f, true, false, ignored);
6048 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6053 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6055 if (_nothing_to_drag || !motion_occurred) {
6059 motion (event, false);
6060 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6061 i->line->end_drag (false, 0);
6064 _editor->commit_reversible_command ();
6068 AutomationRangeDrag::aborted (bool)
6070 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6075 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6077 , initial_time_axis_view (itav)
6079 /* note that time_axis_view may be null if the regionview was created
6080 * as part of a copy operation.
6082 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6083 layer = v->region()->layer ();
6084 initial_y = v->get_canvas_group()->position().y;
6085 initial_playlist = v->region()->playlist ();
6086 initial_position = v->region()->position ();
6087 initial_end = v->region()->position () + v->region()->length ();
6090 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6091 : Drag (e, i->canvas_item ())
6094 , _cumulative_dx (0)
6096 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6097 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6102 PatchChangeDrag::motion (GdkEvent* ev, bool)
6104 framepos_t f = adjusted_current_frame (ev);
6105 boost::shared_ptr<Region> r = _region_view->region ();
6106 f = max (f, r->position ());
6107 f = min (f, r->last_frame ());
6109 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6110 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6111 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6112 _cumulative_dx = dxu;
6116 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6118 if (!movement_occurred) {
6119 if (was_double_click()) {
6120 _region_view->edit_patch_change (_patch_change);
6125 boost::shared_ptr<Region> r (_region_view->region ());
6126 framepos_t f = adjusted_current_frame (ev);
6127 f = max (f, r->position ());
6128 f = min (f, r->last_frame ());
6130 _region_view->move_patch_change (
6132 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6137 PatchChangeDrag::aborted (bool)
6139 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6143 PatchChangeDrag::setup_pointer_frame_offset ()
6145 boost::shared_ptr<Region> region = _region_view->region ();
6146 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6149 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6150 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6157 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6159 _region_view->update_drag_selection (
6161 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6165 MidiRubberbandSelectDrag::deselect_things ()
6170 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6171 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6174 _vertical_only = true;
6178 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6180 double const y = _region_view->midi_view()->y_position ();
6182 y1 = max (0.0, y1 - y);
6183 y2 = max (0.0, y2 - y);
6185 _region_view->update_vertical_drag_selection (
6188 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6193 MidiVerticalSelectDrag::deselect_things ()
6198 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6199 : RubberbandSelectDrag (e, i)
6205 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6207 if (drag_in_progress) {
6208 /* We just want to select things at the end of the drag, not during it */
6212 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6214 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6216 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6218 _editor->commit_reversible_selection_op ();
6222 EditorRubberbandSelectDrag::deselect_things ()
6224 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6226 _editor->selection->clear_tracks();
6227 _editor->selection->clear_regions();
6228 _editor->selection->clear_points ();
6229 _editor->selection->clear_lines ();
6230 _editor->selection->clear_midi_notes ();
6232 _editor->commit_reversible_selection_op();
6235 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6240 _note[0] = _note[1] = 0;
6243 NoteCreateDrag::~NoteCreateDrag ()
6249 NoteCreateDrag::grid_frames (framepos_t t) const
6252 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6253 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6255 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6256 - _region_view->region_beats_to_region_frames (t_beats);
6260 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6262 Drag::start_grab (event, cursor);
6264 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6265 TempoMap& map (_editor->session()->tempo_map());
6267 const framepos_t pf = _drags->current_pointer_frame ();
6268 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6270 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6272 double eqaf = map.exact_qn_at_frame (pf, divisions);
6274 if (divisions != 0) {
6276 const double qaf = map.quarter_note_at_frame (pf);
6278 /* Hack so that we always snap to the note that we are over, instead of snapping
6279 to the next one if we're more than halfway through the one we're over.
6282 const double rem = eqaf - qaf;
6284 eqaf -= grid_beats.to_double();
6288 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6289 /* minimum initial length is grid beats */
6290 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6292 double const x0 = _editor->sample_to_pixel (_note[0]);
6293 double const x1 = _editor->sample_to_pixel (_note[1]);
6294 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6296 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6297 _drag_rect->set_outline_all ();
6298 _drag_rect->set_outline_color (0xffffff99);
6299 _drag_rect->set_fill_color (0xffffff66);
6303 NoteCreateDrag::motion (GdkEvent* event, bool)
6305 TempoMap& map (_editor->session()->tempo_map());
6306 const framepos_t pf = _drags->current_pointer_frame ();
6307 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6308 double eqaf = map.exact_qn_at_frame (pf, divisions);
6310 if (divisions != 0) {
6312 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6314 const double qaf = map.quarter_note_at_frame (pf);
6315 /* Hack so that we always snap to the note that we are over, instead of snapping
6316 to the next one if we're more than halfway through the one we're over.
6319 const double rem = eqaf - qaf;
6321 eqaf -= grid_beats.to_double();
6324 eqaf += grid_beats.to_double();
6326 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6328 double const x0 = _editor->sample_to_pixel (_note[0]);
6329 double const x1 = _editor->sample_to_pixel (_note[1]);
6330 _drag_rect->set_x0 (std::min(x0, x1));
6331 _drag_rect->set_x1 (std::max(x0, x1));
6335 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6337 /* we create a note even if there was no movement */
6338 framepos_t const start = min (_note[0], _note[1]);
6339 framepos_t const start_sess_rel = start + _region_view->region()->position();
6340 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6341 framecnt_t const g = grid_frames (start);
6343 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6347 TempoMap& map (_editor->session()->tempo_map());
6348 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6349 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6351 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6355 NoteCreateDrag::y_to_region (double y) const
6358 _region_view->get_canvas_group()->canvas_to_item (x, y);
6363 NoteCreateDrag::aborted (bool)
6368 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6376 HitCreateDrag::~HitCreateDrag ()
6381 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6383 Drag::start_grab (event, cursor);
6385 TempoMap& map (_editor->session()->tempo_map());
6387 const framepos_t pf = _drags->current_pointer_frame ();
6388 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6390 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6392 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6394 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6398 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6399 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6401 Evoral::Beats length = _region_view->get_grid_beats (pf);
6403 _region_view->create_note_at (start, y, length, event->button.state, false);
6410 HitCreateDrag::motion (GdkEvent* event, bool)
6412 TempoMap& map (_editor->session()->tempo_map());
6414 const framepos_t pf = _drags->current_pointer_frame ();
6415 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6417 if (divisions == 0) {
6421 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6422 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6423 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6425 if (_last_pos == start && y == _last_y) {
6429 Evoral::Beats length = _region_view->get_grid_beats (pf);
6431 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6432 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6436 _region_view->create_note_at (start, y, length, event->button.state, false);
6443 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6449 HitCreateDrag::y_to_region (double y) const
6452 _region_view->get_canvas_group()->canvas_to_item (x, y);
6457 HitCreateDrag::aborted (bool)
6462 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6467 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6471 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6473 Drag::start_grab (event, cursor);
6477 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6483 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6486 distance = _drags->current_pointer_x() - grab_x();
6487 len = ar->fade_in()->back()->when;
6489 distance = grab_x() - _drags->current_pointer_x();
6490 len = ar->fade_out()->back()->when;
6493 /* how long should it be ? */
6495 new_length = len + _editor->pixel_to_sample (distance);
6497 /* now check with the region that this is legal */
6499 new_length = ar->verify_xfade_bounds (new_length, start);
6502 arv->reset_fade_in_shape_width (ar, new_length);
6504 arv->reset_fade_out_shape_width (ar, new_length);
6509 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6515 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6518 distance = _drags->current_pointer_x() - grab_x();
6519 len = ar->fade_in()->back()->when;
6521 distance = grab_x() - _drags->current_pointer_x();
6522 len = ar->fade_out()->back()->when;
6525 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6527 _editor->begin_reversible_command ("xfade trim");
6528 ar->playlist()->clear_owned_changes ();
6531 ar->set_fade_in_length (new_length);
6533 ar->set_fade_out_length (new_length);
6536 /* Adjusting the xfade may affect other regions in the playlist, so we need
6537 to get undo Commands from the whole playlist rather than just the
6541 vector<Command*> cmds;
6542 ar->playlist()->rdiff (cmds);
6543 _editor->session()->add_commands (cmds);
6544 _editor->commit_reversible_command ();
6549 CrossfadeEdgeDrag::aborted (bool)
6552 // arv->redraw_start_xfade ();
6554 // arv->redraw_end_xfade ();
6558 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6559 : Drag (e, item, true)
6560 , line (new EditorCursor (*e))
6562 line->set_position (pos);
6564 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6567 RegionCutDrag::~RegionCutDrag ()
6573 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6575 Drag::start_grab (event, c);
6576 motion (event, false);
6580 RegionCutDrag::motion (GdkEvent* event, bool)
6582 framepos_t pos = _drags->current_pointer_frame();
6583 _editor->snap_to_with_modifier (pos, event);
6585 line->set_position (pos);
6589 RegionCutDrag::finished (GdkEvent* event, bool)
6591 _editor->get_track_canvas()->canvas()->re_enter();
6593 framepos_t pos = _drags->current_pointer_frame();
6594 _editor->snap_to_with_modifier (pos, event);
6598 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6604 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state),
6609 RegionCutDrag::aborted (bool)
6613 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6614 : Drag (e, item, true)
6619 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6621 Drag::start_grab (event, c);
6623 framepos_t where = _editor->canvas_event_sample(event);
6625 _editor->_dragging_playhead = true;
6627 _editor->playhead_cursor->set_position (where);
6631 RulerZoomDrag::motion (GdkEvent* event, bool)
6633 framepos_t where = _editor->canvas_event_sample(event);
6635 _editor->playhead_cursor->set_position (where);
6637 const double movement_limit = 20.0;
6638 const double scale = 1.08;
6639 const double y_delta = last_pointer_y() - current_pointer_y();
6641 if (y_delta > 0 && y_delta < movement_limit) {
6642 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6643 } else if (y_delta < 0 && y_delta > -movement_limit) {
6644 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6649 RulerZoomDrag::finished (GdkEvent*, bool)
6651 _editor->_dragging_playhead = false;
6653 Session* s = _editor->session ();
6655 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6656 _editor->_pending_locate_request = true;
6662 RulerZoomDrag::aborted (bool)