2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_x (0.0)
86 , _current_pointer_y (0.0)
87 , _current_pointer_frame (0)
88 , _old_follow_playhead (false)
92 DragManager::~DragManager ()
97 /** Call abort for each active drag */
103 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 if (!_drags.empty ()) {
109 _editor->set_follow_playhead (_old_follow_playhead, false);
113 _editor->abort_reversible_command();
119 DragManager::add (Drag* d)
121 d->set_manager (this);
122 _drags.push_back (d);
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
128 d->set_manager (this);
129 _drags.push_back (d);
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
136 /* Prevent follow playhead during the drag to be nice to the user */
137 _old_follow_playhead = _editor->follow_playhead ();
138 _editor->set_follow_playhead (false);
140 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
142 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 (*i)->start_grab (e, c);
147 /** Call end_grab for each active drag.
148 * @return true if any drag reported movement having occurred.
151 DragManager::end_grab (GdkEvent* e)
156 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157 bool const t = (*i)->end_grab (e);
168 _editor->set_follow_playhead (_old_follow_playhead, false);
174 DragManager::mark_double_click ()
176 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177 (*i)->set_double_click (true);
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
186 /* calling this implies that we expect the event to have canvas
189 * Can we guarantee that this is true?
192 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
194 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195 bool const t = (*i)->motion_handler (e, from_autoscroll);
196 /* run all handlers; return true if at least one of them
197 returns true (indicating that the event has been handled).
209 DragManager::have_item (ArdourCanvas::Item* i) const
211 list<Drag*>::const_iterator j = _drags.begin ();
212 while (j != _drags.end() && (*j)->item () != i) {
216 return j != _drags.end ();
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
223 , _pointer_frame_offset (0)
224 , _x_constrained (false)
225 , _y_constrained (false)
226 , _was_rolling (false)
227 , _trackview_only (trackview_only)
228 , _move_threshold_passed (false)
229 , _starting_point_passed (false)
230 , _initially_vertical (false)
231 , _was_double_click (false)
234 , _last_pointer_x (0.0)
235 , _last_pointer_y (0.0)
236 , _raw_grab_frame (0)
238 , _last_pointer_frame (0)
240 , _constraint_pressed (false)
246 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
252 _cursor_ctx = CursorContext::create (*_editor, cursor);
254 _cursor_ctx->change (cursor);
261 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
264 /* we set up x/y dragging constraints on first move */
265 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
267 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
269 setup_pointer_frame_offset ();
270 _grab_frame = adjusted_frame (_raw_grab_frame, event);
271 _last_pointer_frame = _grab_frame;
272 _last_pointer_x = _grab_x;
274 if (_trackview_only) {
275 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
278 _last_pointer_y = _grab_y;
282 if (!_editor->cursors()->is_invalid (cursor)) {
283 /* CAIROCANVAS need a variant here that passes *cursor */
284 _cursor_ctx = CursorContext::create (*_editor, cursor);
287 if (_editor->session() && _editor->session()->transport_rolling()) {
290 _was_rolling = false;
293 switch (_editor->snap_type()) {
294 case SnapToRegionStart:
295 case SnapToRegionEnd:
296 case SnapToRegionSync:
297 case SnapToRegionBoundary:
298 _editor->build_region_boundary_cache ();
305 /** Call to end a drag `successfully'. Ungrabs item and calls
306 * subclass' finished() method.
308 * @param event GDK event, or 0.
309 * @return true if some movement occurred, otherwise false.
312 Drag::end_grab (GdkEvent* event)
314 _editor->stop_canvas_autoscroll ();
318 finished (event, _move_threshold_passed);
320 _editor->verbose_cursor()->hide ();
323 return _move_threshold_passed;
327 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
331 if (f > _pointer_frame_offset) {
332 pos = f - _pointer_frame_offset;
336 _editor->snap_to_with_modifier (pos, event);
343 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
345 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
349 Drag::snap_delta (guint state) const
351 if (ArdourKeyboard::indicates_snap_delta (state)) {
359 Drag::current_pointer_x() const
361 return _drags->current_pointer_x ();
365 Drag::current_pointer_y () const
367 if (!_trackview_only) {
368 return _drags->current_pointer_y ();
371 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
375 Drag::setup_snap_delta (framepos_t pos)
377 framepos_t temp = pos;
378 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
379 _snap_delta = temp - pos;
383 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
385 /* check to see if we have moved in any way that matters since the last motion event */
386 if (_move_threshold_passed &&
387 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
388 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
392 pair<framecnt_t, int> const threshold = move_threshold ();
394 bool const old_move_threshold_passed = _move_threshold_passed;
396 if (!_move_threshold_passed) {
398 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
399 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
401 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
404 if (active (_editor->mouse_mode) && _move_threshold_passed) {
406 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
408 if (old_move_threshold_passed != _move_threshold_passed) {
412 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
413 _initially_vertical = true;
415 _initially_vertical = false;
417 /** check constraints for this drag.
418 * Note that the current convention is to use "contains" for
419 * key modifiers during motion and "equals" when initiating a drag.
420 * In this case we haven't moved yet, so "equals" applies here.
422 if (Config->get_edit_mode() != Lock) {
423 if (event->motion.state & Gdk::BUTTON2_MASK) {
424 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
425 if (_constraint_pressed) {
426 _x_constrained = false;
427 _y_constrained = true;
429 _x_constrained = true;
430 _y_constrained = false;
432 } else if (_constraint_pressed) {
433 // if dragging normally, the motion is constrained to the first direction of movement.
434 if (_initially_vertical) {
435 _x_constrained = true;
436 _y_constrained = false;
438 _x_constrained = false;
439 _y_constrained = true;
443 if (event->button.state & Gdk::BUTTON2_MASK) {
444 _x_constrained = false;
446 _x_constrained = true;
448 _y_constrained = false;
452 if (!from_autoscroll) {
453 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
456 if (!_editor->autoscroll_active() || from_autoscroll) {
459 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
461 motion (event, first_move && !_starting_point_passed);
463 if (first_move && !_starting_point_passed) {
464 _starting_point_passed = true;
467 _last_pointer_x = _drags->current_pointer_x ();
468 _last_pointer_y = current_pointer_y ();
469 _last_pointer_frame = adjusted_current_frame (event, false);
479 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
487 aborted (_move_threshold_passed);
489 _editor->stop_canvas_autoscroll ();
490 _editor->verbose_cursor()->hide ();
494 Drag::show_verbose_cursor_time (framepos_t frame)
496 _editor->verbose_cursor()->set_time (frame);
497 _editor->verbose_cursor()->show ();
501 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
503 _editor->verbose_cursor()->set_duration (start, end);
504 _editor->verbose_cursor()->show ();
508 Drag::show_verbose_cursor_text (string const & text)
510 _editor->verbose_cursor()->set (text);
511 _editor->verbose_cursor()->show ();
514 boost::shared_ptr<Region>
515 Drag::add_midi_region (MidiTimeAxisView* view, bool commit, const int32_t sub_num)
517 if (_editor->session()) {
518 const TempoMap& map (_editor->session()->tempo_map());
519 framecnt_t pos = grab_frame();
520 /* not that the frame rate used here can be affected by pull up/down which
523 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
524 return view->add_region (grab_frame(), len, commit, sub_num);
527 return boost::shared_ptr<Region>();
530 struct PresentationInfoTimeAxisViewSorter {
531 bool operator() (TimeAxisView* a, TimeAxisView* b) {
532 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
536 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
541 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
543 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
544 as some of the regions we are dragging may be on such tracks.
547 TrackViewList track_views = _editor->track_views;
548 track_views.sort (PresentationInfoTimeAxisViewSorter ());
550 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
551 _time_axis_views.push_back (*i);
553 TimeAxisView::Children children_list = (*i)->get_child_list ();
554 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
555 _time_axis_views.push_back (j->get());
559 /* the list of views can be empty at this point if this is a region list-insert drag
562 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
563 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
566 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
570 RegionDrag::region_going_away (RegionView* v)
572 list<DraggingView>::iterator i = _views.begin ();
573 while (i != _views.end() && i->view != v) {
577 if (i != _views.end()) {
582 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
583 * or -1 if it is not found.
586 RegionDrag::find_time_axis_view (TimeAxisView* t) const
589 int const N = _time_axis_views.size ();
590 while (i < N && _time_axis_views[i] != t) {
594 if (_time_axis_views[i] != t) {
601 /** determines if @pos is the closest frame to an exact musical division when using SnapMagnetic.
602 * (SnapMagnetic may snap to an exact division or no division at all).
603 * this is a hotfix for musical region position/trim. we should really
604 * deliver musical divisors when snapping magnetically to avoid this.
607 RegionDrag::current_music_divisor (framepos_t pos, int32_t button_state)
609 int32_t divisions = _editor->get_grid_music_divisions (button_state);
611 if (_editor->snap_mode() == Editing::SnapMagnetic && !ArdourKeyboard::indicates_snap (button_state)) {
612 TempoMap& tmap (_editor->session()->tempo_map());
613 const framepos_t exact_qn_pos = tmap.frame_at_quarter_note (tmap.exact_qn_at_frame (pos, divisions));
615 if (pos != exact_qn_pos) {
616 /* magnetic has not snapped */
624 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
625 : RegionDrag (e, i, p, v)
627 , _ignore_video_lock (false)
629 , _last_pointer_time_axis_view (0)
630 , _last_pointer_layer (0)
635 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
639 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
641 Drag::start_grab (event, cursor);
642 setup_snap_delta (_last_frame_position);
644 show_verbose_cursor_time (_last_frame_position);
646 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
648 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
649 assert(_last_pointer_time_axis_view >= 0);
650 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
653 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
654 _ignore_video_lock = true;
658 /* cross track dragging seems broken here. disabled for now. */
659 _y_constrained = true;
664 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
666 /* compute the amount of pointer motion in frames, and where
667 the region would be if we moved it by that much.
669 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
671 framepos_t sync_frame;
672 framecnt_t sync_offset;
675 sync_offset = _primary->region()->sync_offset (sync_dir);
677 /* we don't handle a sync point that lies before zero.
679 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
681 framecnt_t const sd = snap_delta (event->button.state);
682 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
684 _editor->snap_to_with_modifier (sync_frame, event);
686 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
689 *pending_region_position = _last_frame_position;
692 if (*pending_region_position > max_framepos - _primary->region()->length()) {
693 *pending_region_position = _last_frame_position;
698 bool const x_move_allowed = !_x_constrained;
700 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
702 /* x movement since last time (in pixels) */
703 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
705 /* total x movement */
706 framecnt_t total_dx = *pending_region_position;
707 if (regions_came_from_canvas()) {
708 total_dx = total_dx - grab_frame ();
711 /* check that no regions have gone off the start of the session */
712 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
713 if ((i->view->region()->position() + total_dx) < 0) {
715 *pending_region_position = _last_frame_position;
726 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
732 const int tavsize = _time_axis_views.size();
733 const int dt = delta > 0 ? +1 : -1;
735 int target = start + delta - skip;
737 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
738 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
740 while (current >= 0 && current != target) {
742 if (current < 0 && dt < 0) {
745 if (current >= tavsize && dt > 0) {
748 if (current < 0 || current >= tavsize) {
752 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
753 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
757 if (distance_only && current == start + delta) {
765 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
767 if (_y_constrained) {
771 const int tavsize = _time_axis_views.size();
772 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
773 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
774 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
776 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
777 /* already in the drop zone */
778 if (delta_track >= 0) {
779 /* downward motion - OK if others are still not in the dropzone */
788 } else if (n >= tavsize) {
789 /* downward motion into drop zone. That's fine. */
793 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
794 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
795 /* not a track, or the wrong type */
799 double const l = i->layer + delta_layer;
801 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
802 mode to allow the user to place a region below another on layer 0.
804 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
805 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
806 If it has, the layers will be munged later anyway, so it's ok.
812 /* all regions being dragged are ok with this change */
816 struct DraggingViewSorter {
817 bool operator() (const DraggingView& a, const DraggingView& b) {
818 return a.time_axis_view < b.time_axis_view;
823 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
825 double delta_layer = 0;
826 int delta_time_axis_view = 0;
827 int current_pointer_time_axis_view = -1;
829 assert (!_views.empty ());
831 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
833 /* Find the TimeAxisView that the pointer is now over */
834 const double cur_y = current_pointer_y ();
835 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
836 TimeAxisView* tv = r.first;
838 if (!tv && cur_y < 0) {
839 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
843 /* find drop-zone y-position */
844 Coord last_track_bottom_edge;
845 last_track_bottom_edge = 0;
846 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
847 if (!(*t)->hidden()) {
848 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
853 if (tv && tv->view()) {
854 /* the mouse is over a track */
855 double layer = r.second;
857 if (first_move && tv->view()->layer_display() == Stacked) {
858 tv->view()->set_layer_display (Expanded);
861 /* Here's the current pointer position in terms of time axis view and layer */
862 current_pointer_time_axis_view = find_time_axis_view (tv);
863 assert(current_pointer_time_axis_view >= 0);
865 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
867 /* Work out the change in y */
869 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
870 if (!rtv || !rtv->is_track()) {
871 /* ignore non-tracks early on. we can't move any regions on them */
872 } else if (_last_pointer_time_axis_view < 0) {
873 /* Was in the drop-zone, now over a track.
874 * Hence it must be an upward move (from the bottom)
876 * track_index is still -1, so delta must be set to
877 * move up the correct number of tracks from the bottom.
879 * This is necessary because steps may be skipped if
880 * the bottom-most track is not a valid target and/or
881 * if there are hidden tracks at the bottom.
882 * Hence the initial offset (_ddropzone) as well as the
883 * last valid pointer position (_pdropzone) need to be
884 * taken into account.
886 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
888 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
891 /* TODO needs adjustment per DraggingView,
893 * e.g. select one region on the top-layer of a track
894 * and one region which is at the bottom-layer of another track
897 * Indicated drop-zones and layering is wrong.
898 * and may infer additional layers on the target-track
899 * (depending how many layers the original track had).
901 * Or select two regions (different layers) on a same track,
902 * move across a non-layer track.. -> layering info is lost.
903 * on drop either of the regions may be on top.
905 * Proposed solution: screw it :) well,
906 * don't use delta_layer, use an absolute value
907 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
908 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
909 * 3) iterate over all DraggingView, find the one that is over the track with most layers
910 * 4) proportionally scale layer to layers available on target
912 delta_layer = current_pointer_layer - _last_pointer_layer;
915 /* for automation lanes, there is a TimeAxisView but no ->view()
916 * if (!tv) -> dropzone
918 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
919 /* Moving into the drop-zone.. */
920 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
921 /* delta_time_axis_view may not be sufficient to move into the DZ
922 * the mouse may enter it, but it may not be a valid move due to
925 * -> remember the delta needed to move into the dropzone
927 _ddropzone = delta_time_axis_view;
928 /* ..but subtract hidden tracks (or routes) at the bottom.
929 * we silently move mover them
931 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
932 - _time_axis_views.size();
934 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
935 /* move around inside the zone.
936 * This allows to move further down until all regions are in the zone.
938 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
939 assert(ptr_y >= last_track_bottom_edge);
940 assert(_ddropzone > 0);
942 /* calculate mouse position in 'tracks' below last track. */
943 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
944 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
946 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
948 delta_time_axis_view = dzpos - _pdropzone;
949 } else if (dzpos < _pdropzone && _ndropzone > 0) {
950 // move up inside the DZ
951 delta_time_axis_view = dzpos - _pdropzone;
955 /* Work out the change in x */
956 framepos_t pending_region_position;
957 double const x_delta = compute_x_delta (event, &pending_region_position);
958 _last_frame_position = pending_region_position;
960 /* calculate hidden tracks in current y-axis delta */
962 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
963 /* The mouse is more than one track below the dropzone.
964 * distance calculation is not needed (and would not work, either
965 * because the dropzone is "packed").
967 * Except when [partially] moving regions out of dropzone in a large step.
968 * (the mouse may or may not remain in the DZ)
969 * Hidden tracks at the bottom of the TAV need to be skipped.
971 * This also handles the case if the mouse entered the DZ
972 * in a large step (exessive delta), either due to fast-movement,
973 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
975 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
976 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
978 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
979 -_time_axis_views.size() - dt;
982 else if (_last_pointer_time_axis_view < 0) {
983 /* Moving out of the zone. Check for hidden tracks at the bottom. */
984 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
985 -_time_axis_views.size() - delta_time_axis_view;
987 /* calculate hidden tracks that are skipped by the pointer movement */
988 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
989 - _last_pointer_time_axis_view
990 - delta_time_axis_view;
993 /* Verify change in y */
994 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
995 /* this y movement is not allowed, so do no y movement this time */
996 delta_time_axis_view = 0;
1001 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
1002 /* haven't reached next snap point, and we're not switching
1003 trackviews nor layers. nothing to do.
1008 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
1009 PlaylistDropzoneMap playlist_dropzone_map;
1010 _ndropzone = 0; // number of elements currently in the dropzone
1013 /* sort views by time_axis.
1014 * This retains track order in the dropzone, regardless
1015 * of actual selection order
1017 _views.sort (DraggingViewSorter());
1019 /* count number of distinct tracks of all regions
1020 * being dragged, used for dropzone.
1022 int prev_track = -1;
1023 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1024 if (i->time_axis_view != prev_track) {
1025 prev_track = i->time_axis_view;
1031 _views.back().time_axis_view -
1032 _views.front().time_axis_view;
1034 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1035 - _views.back().time_axis_view;
1037 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1041 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1043 RegionView* rv = i->view;
1048 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1055 /* reparent the regionview into a group above all
1059 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1060 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1061 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1062 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1063 /* move the item so that it continues to appear at the
1064 same location now that its parent has changed.
1066 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1069 /* If we have moved tracks, we'll fudge the layer delta so that the
1070 region gets moved back onto layer 0 on its new track; this avoids
1071 confusion when dragging regions from non-zero layers onto different
1074 double this_delta_layer = delta_layer;
1075 if (delta_time_axis_view != 0) {
1076 this_delta_layer = - i->layer;
1079 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1081 int track_index = i->time_axis_view + this_delta_time_axis_view;
1082 assert(track_index >= 0);
1084 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1085 /* Track is in the Dropzone */
1087 i->time_axis_view = track_index;
1088 assert(i->time_axis_view >= (int) _time_axis_views.size());
1091 double yposition = 0;
1092 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1093 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1096 /* store index of each new playlist as a negative count, starting at -1 */
1098 if (pdz == playlist_dropzone_map.end()) {
1099 /* compute where this new track (which doesn't exist yet) will live
1102 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1104 /* How high is this region view ? */
1106 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1107 ArdourCanvas::Rect bbox;
1110 bbox = obbox.get ();
1113 last_track_bottom_edge += bbox.height();
1115 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1118 yposition = pdz->second;
1121 /* values are zero or negative, hence the use of min() */
1122 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1125 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1127 mrv->apply_note_range (60, 71, true);
1131 /* The TimeAxisView that this region is now over */
1132 TimeAxisView* current_tv = _time_axis_views[track_index];
1134 /* Ensure it is moved from stacked -> expanded if appropriate */
1135 if (current_tv->view()->layer_display() == Stacked) {
1136 current_tv->view()->set_layer_display (Expanded);
1139 /* We're only allowed to go -ve in layer on Expanded views */
1140 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1141 this_delta_layer = - i->layer;
1145 rv->set_height (current_tv->view()->child_height ());
1147 /* Update show/hidden status as the region view may have come from a hidden track,
1148 or have moved to one.
1150 if (current_tv->hidden ()) {
1151 rv->get_canvas_group()->hide ();
1153 rv->get_canvas_group()->show ();
1156 /* Update the DraggingView */
1157 i->time_axis_view = track_index;
1158 i->layer += this_delta_layer;
1161 _editor->mouse_brush_insert_region (rv, pending_region_position);
1165 /* Get the y coordinate of the top of the track that this region is now over */
1166 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1168 /* And adjust for the layer that it should be on */
1169 StreamView* cv = current_tv->view ();
1170 switch (cv->layer_display ()) {
1174 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1177 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1181 /* need to get the parent of the regionview
1182 * canvas group and get its position in
1183 * equivalent coordinate space as the trackview
1184 * we are now dragging over.
1187 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1191 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1193 MidiStreamView* msv;
1194 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1195 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1200 /* Now move the region view */
1201 rv->move (x_delta, y_delta);
1203 } /* foreach region */
1205 _total_x_delta += x_delta;
1207 if (x_delta != 0 && !_brushing) {
1208 show_verbose_cursor_time (_last_frame_position);
1211 /* keep track of pointer movement */
1213 /* the pointer is currently over a time axis view */
1215 if (_last_pointer_time_axis_view < 0) {
1216 /* last motion event was not over a time axis view
1217 * or last y-movement out of the dropzone was not valid
1220 if (delta_time_axis_view < 0) {
1221 /* in the drop zone, moving up */
1223 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1224 * We do not use negative _last_pointer_time_axis_view because
1225 * the dropzone is "packed" (the actual track offset is ignored)
1227 * As opposed to the actual number
1228 * of elements in the dropzone (_ndropzone)
1229 * _pdropzone is not constrained. This is necessary
1230 * to allow moving multiple regions with y-distance
1233 * There can be 0 elements in the dropzone,
1234 * even though the drag-pointer is inside the DZ.
1237 * [ Audio-track, Midi-track, Audio-track, DZ ]
1238 * move regions from both audio tracks at the same time into the
1239 * DZ by grabbing the region in the bottom track.
1241 assert(current_pointer_time_axis_view >= 0);
1242 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1246 /* only move out of the zone if the movement is OK */
1247 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1248 assert(delta_time_axis_view < 0);
1249 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1250 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1251 * the current position can be calculated as follows:
1253 // a well placed oofus attack can still throw this off.
1254 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1255 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1258 /* last motion event was also over a time axis view */
1259 _last_pointer_time_axis_view += delta_time_axis_view;
1260 assert(_last_pointer_time_axis_view >= 0);
1265 /* the pointer is not over a time axis view */
1266 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1267 _pdropzone += delta_time_axis_view - delta_skip;
1268 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1271 _last_pointer_layer += delta_layer;
1275 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1277 if (_copy && first_move) {
1278 if (_x_constrained && !_brushing) {
1279 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1280 } else if (!_brushing) {
1281 _editor->begin_reversible_command (Operations::region_copy);
1282 } else if (_brushing) {
1283 _editor->begin_reversible_command (Operations::drag_region_brush);
1285 /* duplicate the regionview(s) and region(s) */
1287 list<DraggingView> new_regionviews;
1289 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1291 RegionView* rv = i->view;
1292 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1293 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1295 const boost::shared_ptr<const Region> original = rv->region();
1296 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true
1297 , current_music_divisor (original->position(), event->button.state));
1298 /* need to set this so that the drop zone code can work. This doesn't
1299 actually put the region into the playlist, but just sets a weak pointer
1302 region_copy->set_playlist (original->playlist());
1306 boost::shared_ptr<AudioRegion> audioregion_copy
1307 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1309 nrv = new AudioRegionView (*arv, audioregion_copy);
1311 boost::shared_ptr<MidiRegion> midiregion_copy
1312 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1313 nrv = new MidiRegionView (*mrv, midiregion_copy);
1318 nrv->get_canvas_group()->show ();
1319 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1321 /* swap _primary to the copy */
1323 if (rv == _primary) {
1327 /* ..and deselect the one we copied */
1329 rv->set_selected (false);
1332 if (!new_regionviews.empty()) {
1334 /* reflect the fact that we are dragging the copies */
1336 _views = new_regionviews;
1338 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1341 } else if (!_copy && first_move) {
1342 if (_x_constrained && !_brushing) {
1343 _editor->begin_reversible_command (_("fixed time region drag"));
1344 } else if (!_brushing) {
1345 _editor->begin_reversible_command (Operations::region_drag);
1346 } else if (_brushing) {
1347 _editor->begin_reversible_command (Operations::drag_region_brush);
1350 RegionMotionDrag::motion (event, first_move);
1354 RegionMotionDrag::finished (GdkEvent *, bool)
1356 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1357 if (!(*i)->view()) {
1361 if ((*i)->view()->layer_display() == Expanded) {
1362 (*i)->view()->set_layer_display (Stacked);
1368 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1370 RegionMotionDrag::finished (ev, movement_occurred);
1372 if (!movement_occurred) {
1376 if (was_double_click() && !_views.empty()) {
1377 DraggingView dv = _views.front();
1378 dv.view->show_region_editor ();
1385 assert (!_views.empty ());
1387 /* We might have hidden region views so that they weren't visible during the drag
1388 (when they have been reparented). Now everything can be shown again, as region
1389 views are back in their track parent groups.
1391 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1392 i->view->get_canvas_group()->show ();
1395 bool const changed_position = (_last_frame_position != _primary->region()->position());
1396 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1397 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1419 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1423 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1425 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1430 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1431 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1432 uint32_t output_chan = region->n_channels();
1433 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1434 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1436 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1437 TimeAxisView* tav =_editor->axis_view_from_stripable (audio_tracks.front());
1439 tav->set_height (original->current_height());
1441 return dynamic_cast<RouteTimeAxisView*>(tav);
1443 ChanCount one_midi_port (DataType::MIDI, 1);
1444 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1445 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1446 (ARDOUR::Plugin::PresetRecord*) 0,
1447 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1448 TimeAxisView* tav = _editor->axis_view_from_stripable (midi_tracks.front());
1450 tav->set_height (original->current_height());
1452 return dynamic_cast<RouteTimeAxisView*> (tav);
1455 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1461 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state)
1463 RegionSelection new_views;
1464 PlaylistSet modified_playlists;
1465 RouteTimeAxisView* new_time_axis_view = 0;
1468 /* all changes were made during motion event handlers */
1470 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1474 _editor->commit_reversible_command ();
1478 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1479 PlaylistMapping playlist_mapping;
1481 /* insert the regions into their new playlists */
1482 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1484 RouteTimeAxisView* dest_rtv = 0;
1486 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1492 if (changed_position && !_x_constrained) {
1493 where = i->view->region()->position() - drag_delta;
1495 where = i->view->region()->position();
1498 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1499 /* dragged to drop zone */
1501 PlaylistMapping::iterator pm;
1503 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1504 /* first region from this original playlist: create a new track */
1505 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1506 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1507 dest_rtv = new_time_axis_view;
1509 /* we already created a new track for regions from this playlist, use it */
1510 dest_rtv = pm->second;
1513 /* destination time axis view is the one we dragged to */
1514 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1517 if (dest_rtv != 0) {
1518 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1519 modified_playlists, current_music_divisor (where, ev_state));
1521 if (new_view != 0) {
1522 new_views.push_back (new_view);
1526 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1527 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1530 list<DraggingView>::const_iterator next = i;
1536 /* If we've created new regions either by copying or moving
1537 to a new track, we want to replace the old selection with the new ones
1540 if (new_views.size() > 0) {
1541 _editor->selection->set (new_views);
1544 /* write commands for the accumulated diffs for all our modified playlists */
1545 add_stateful_diff_commands_for_playlists (modified_playlists);
1547 _editor->commit_reversible_command ();
1551 RegionMoveDrag::finished_no_copy (
1552 bool const changed_position,
1553 bool const changed_tracks,
1554 framecnt_t const drag_delta,
1555 int32_t const ev_state
1558 RegionSelection new_views;
1559 PlaylistSet modified_playlists;
1560 PlaylistSet frozen_playlists;
1561 set<RouteTimeAxisView*> views_to_update;
1562 RouteTimeAxisView* new_time_axis_view = 0;
1564 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1565 PlaylistMapping playlist_mapping;
1567 std::set<boost::shared_ptr<const Region> > uniq;
1568 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1570 RegionView* rv = i->view;
1571 RouteTimeAxisView* dest_rtv = 0;
1573 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1578 if (uniq.find (rv->region()) != uniq.end()) {
1579 /* prevent duplicate moves when selecting regions from shared playlists */
1583 uniq.insert(rv->region());
1585 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1586 /* dragged to drop zone */
1588 PlaylistMapping::iterator pm;
1590 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1591 /* first region from this original playlist: create a new track */
1592 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1593 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1594 dest_rtv = new_time_axis_view;
1596 /* we already created a new track for regions from this playlist, use it */
1597 dest_rtv = pm->second;
1601 /* destination time axis view is the one we dragged to */
1602 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1607 double const dest_layer = i->layer;
1609 views_to_update.insert (dest_rtv);
1613 if (changed_position && !_x_constrained) {
1614 where = rv->region()->position() - drag_delta;
1616 where = rv->region()->position();
1619 if (changed_tracks) {
1621 /* insert into new playlist */
1623 RegionView* new_view = insert_region_into_playlist (
1624 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1625 modified_playlists, current_music_divisor (where, ev_state)
1628 if (new_view == 0) {
1633 new_views.push_back (new_view);
1635 /* remove from old playlist */
1637 /* the region that used to be in the old playlist is not
1638 moved to the new one - we use a copy of it. as a result,
1639 any existing editor for the region should no longer be
1642 rv->hide_region_editor();
1645 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1649 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1651 /* this movement may result in a crossfade being modified, or a layering change,
1652 so we need to get undo data from the playlist as well as the region.
1655 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1657 playlist->clear_changes ();
1660 rv->region()->clear_changes ();
1663 motion on the same track. plonk the previously reparented region
1664 back to its original canvas group (its streamview).
1665 No need to do anything for copies as they are fake regions which will be deleted.
1668 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1669 rv->get_canvas_group()->set_y_position (i->initial_y);
1672 /* just change the model */
1673 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1674 playlist->set_layer (rv->region(), dest_layer);
1677 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1679 r = frozen_playlists.insert (playlist);
1682 playlist->freeze ();
1685 rv->region()->set_position (where, current_music_divisor (where, ev_state));
1686 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1689 if (changed_tracks) {
1691 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1692 was selected in all of them, then removing it from a playlist will have removed all
1693 trace of it from _views (i.e. there were N regions selected, we removed 1,
1694 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1695 corresponding regionview, and _views is now empty).
1697 This could have invalidated any and all iterators into _views.
1699 The heuristic we use here is: if the region selection is empty, break out of the loop
1700 here. if the region selection is not empty, then restart the loop because we know that
1701 we must have removed at least the region(view) we've just been working on as well as any
1702 that we processed on previous iterations.
1704 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1705 we can just iterate.
1709 if (_views.empty()) {
1720 /* If we've created new regions either by copying or moving
1721 to a new track, we want to replace the old selection with the new ones
1724 if (new_views.size() > 0) {
1725 _editor->selection->set (new_views);
1728 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1732 /* write commands for the accumulated diffs for all our modified playlists */
1733 add_stateful_diff_commands_for_playlists (modified_playlists);
1734 /* applies to _brushing */
1735 _editor->commit_reversible_command ();
1737 /* We have futzed with the layering of canvas items on our streamviews.
1738 If any region changed layer, this will have resulted in the stream
1739 views being asked to set up their region views, and all will be well.
1740 If not, we might now have badly-ordered region views. Ask the StreamViews
1741 involved to sort themselves out, just in case.
1744 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1745 (*i)->view()->playlist_layered ((*i)->track ());
1749 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1750 * @param region Region to remove.
1751 * @param playlist playlist To remove from.
1752 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1753 * that clear_changes () is only called once per playlist.
1756 RegionMoveDrag::remove_region_from_playlist (
1757 boost::shared_ptr<Region> region,
1758 boost::shared_ptr<Playlist> playlist,
1759 PlaylistSet& modified_playlists
1762 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1765 playlist->clear_changes ();
1768 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1772 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1773 * clearing the playlist's diff history first if necessary.
1774 * @param region Region to insert.
1775 * @param dest_rtv Destination RouteTimeAxisView.
1776 * @param dest_layer Destination layer.
1777 * @param where Destination position.
1778 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1779 * that clear_changes () is only called once per playlist.
1780 * @return New RegionView, or 0 if no insert was performed.
1783 RegionMoveDrag::insert_region_into_playlist (
1784 boost::shared_ptr<Region> region,
1785 RouteTimeAxisView* dest_rtv,
1788 PlaylistSet& modified_playlists,
1789 const int32_t sub_num
1792 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1793 if (!dest_playlist) {
1797 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1798 _new_region_view = 0;
1799 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1801 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1802 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1804 dest_playlist->clear_changes ();
1806 dest_playlist->add_region (region, where, 1.0, false, sub_num);
1808 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1809 dest_playlist->set_layer (region, dest_layer);
1814 assert (_new_region_view);
1816 return _new_region_view;
1820 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1822 _new_region_view = rv;
1826 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1828 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1829 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1831 _editor->session()->add_command (c);
1840 RegionMoveDrag::aborted (bool movement_occurred)
1844 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1845 list<DraggingView>::const_iterator next = i;
1854 RegionMotionDrag::aborted (movement_occurred);
1859 RegionMotionDrag::aborted (bool)
1861 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1863 StreamView* sview = (*i)->view();
1866 if (sview->layer_display() == Expanded) {
1867 sview->set_layer_display (Stacked);
1872 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1873 RegionView* rv = i->view;
1874 TimeAxisView* tv = &(rv->get_time_axis_view ());
1875 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1877 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1878 rv->get_canvas_group()->set_y_position (0);
1880 rv->move (-_total_x_delta, 0);
1881 rv->set_height (rtv->view()->child_height ());
1885 /** @param b true to brush, otherwise false.
1886 * @param c true to make copies of the regions being moved, otherwise false.
1888 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1889 : RegionMotionDrag (e, i, p, v, b)
1891 , _new_region_view (0)
1893 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1896 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1897 if (rtv && rtv->is_track()) {
1898 speed = rtv->track()->speed ();
1901 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1905 RegionMoveDrag::setup_pointer_frame_offset ()
1907 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1910 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1911 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1913 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1915 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1916 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1918 _primary = v->view()->create_region_view (r, false, false);
1920 _primary->get_canvas_group()->show ();
1921 _primary->set_position (pos, 0);
1922 _views.push_back (DraggingView (_primary, this, v));
1924 _last_frame_position = pos;
1926 _item = _primary->get_canvas_group ();
1930 RegionInsertDrag::finished (GdkEvent * event, bool)
1932 int pos = _views.front().time_axis_view;
1933 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1935 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1937 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1938 _primary->get_canvas_group()->set_y_position (0);
1940 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1942 _editor->begin_reversible_command (Operations::insert_region);
1943 playlist->clear_changes ();
1944 playlist->add_region (_primary->region (), _last_frame_position);
1946 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1947 if (Config->get_edit_mode() == Ripple) {
1948 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1951 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1952 _editor->commit_reversible_command ();
1960 RegionInsertDrag::aborted (bool)
1967 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1968 : RegionMoveDrag (e, i, p, v, false, false)
1970 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1973 struct RegionSelectionByPosition {
1974 bool operator() (RegionView*a, RegionView* b) {
1975 return a->region()->position () < b->region()->position();
1980 RegionSpliceDrag::motion (GdkEvent* event, bool)
1982 /* Which trackview is this ? */
1984 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1985 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1987 /* The region motion is only processed if the pointer is over
1991 if (!tv || !tv->is_track()) {
1992 /* To make sure we hide the verbose canvas cursor when the mouse is
1993 not held over an audio track.
1995 _editor->verbose_cursor()->hide ();
1998 _editor->verbose_cursor()->show ();
2003 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2009 RegionSelection copy;
2010 _editor->selection->regions.by_position(copy);
2012 framepos_t const pf = adjusted_current_frame (event);
2014 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2016 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2022 boost::shared_ptr<Playlist> playlist;
2024 if ((playlist = atv->playlist()) == 0) {
2028 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2033 if (pf < (*i)->region()->last_frame() + 1) {
2037 if (pf > (*i)->region()->first_frame()) {
2043 playlist->shuffle ((*i)->region(), dir);
2048 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2050 RegionMoveDrag::finished (event, movement_occurred);
2054 RegionSpliceDrag::aborted (bool)
2064 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2067 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2069 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2070 RegionSelection to_ripple;
2071 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2072 if ((*i)->position() >= where) {
2073 to_ripple.push_back (rtv->view()->find_view(*i));
2077 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2078 if (!exclude.contains (*i)) {
2079 // the selection has already been added to _views
2081 if (drag_in_progress) {
2082 // do the same things that RegionMotionDrag::motion does when
2083 // first_move is true, for the region views that we're adding
2084 // to _views this time
2087 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2088 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2089 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2090 rvg->reparent (_editor->_drag_motion_group);
2092 // we only need to move in the y direction
2093 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2098 _views.push_back (DraggingView (*i, this, tav));
2104 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2107 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2108 // we added all the regions after the selection
2110 std::list<DraggingView>::iterator to_erase = i++;
2111 if (!_editor->selection->regions.contains (to_erase->view)) {
2112 // restore the non-selected regions to their original playlist & positions,
2113 // and then ripple them back by the length of the regions that were dragged away
2114 // do the same things as RegionMotionDrag::aborted
2116 RegionView *rv = to_erase->view;
2117 TimeAxisView* tv = &(rv->get_time_axis_view ());
2118 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2121 // plonk them back onto their own track
2122 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2123 rv->get_canvas_group()->set_y_position (0);
2127 // move the underlying region to match the view
2128 rv->region()->set_position (rv->region()->position() + amount);
2130 // restore the view to match the underlying region's original position
2131 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2134 rv->set_height (rtv->view()->child_height ());
2135 _views.erase (to_erase);
2141 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2143 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2145 return allow_moves_across_tracks;
2153 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2154 : RegionMoveDrag (e, i, p, v, false, false)
2156 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2157 // compute length of selection
2158 RegionSelection selected_regions = _editor->selection->regions;
2159 selection_length = selected_regions.end_frame() - selected_regions.start();
2161 // we'll only allow dragging to another track in ripple mode if all the regions
2162 // being dragged start off on the same track
2163 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2166 exclude = new RegionList;
2167 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2168 exclude->push_back((*i)->region());
2171 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2172 RegionSelection copy;
2173 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2175 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2176 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2178 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2179 // find ripple start point on each applicable playlist
2180 RegionView *first_selected_on_this_track = NULL;
2181 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2182 if ((*i)->region()->playlist() == (*pi)) {
2183 // region is on this playlist - it's the first, because they're sorted
2184 first_selected_on_this_track = *i;
2188 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2189 add_all_after_to_views (
2190 &first_selected_on_this_track->get_time_axis_view(),
2191 first_selected_on_this_track->region()->position(),
2192 selected_regions, false);
2195 if (allow_moves_across_tracks) {
2196 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2204 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2206 /* Which trackview is this ? */
2208 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2209 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2211 /* The region motion is only processed if the pointer is over
2215 if (!tv || !tv->is_track()) {
2216 /* To make sure we hide the verbose canvas cursor when the mouse is
2217 not held over an audiotrack.
2219 _editor->verbose_cursor()->hide ();
2223 framepos_t where = adjusted_current_frame (event);
2224 assert (where >= 0);
2226 double delta = compute_x_delta (event, &after);
2228 framecnt_t amount = _editor->pixel_to_sample (delta);
2230 if (allow_moves_across_tracks) {
2231 // all the originally selected regions were on the same track
2233 framecnt_t adjust = 0;
2234 if (prev_tav && tv != prev_tav) {
2235 // dragged onto a different track
2236 // remove the unselected regions from _views, restore them to their original positions
2237 // and add the regions after the drop point on the new playlist to _views instead.
2238 // undo the effect of rippling the previous playlist, and include the effect of removing
2239 // the dragged region(s) from this track
2241 remove_unselected_from_views (prev_amount, false);
2242 // ripple previous playlist according to the regions that have been removed onto the new playlist
2243 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2246 // move just the selected regions
2247 RegionMoveDrag::motion(event, first_move);
2249 // ensure that the ripple operation on the new playlist inserts selection_length time
2250 adjust = selection_length;
2251 // ripple the new current playlist
2252 tv->playlist()->ripple (where, amount+adjust, exclude);
2254 // add regions after point where drag entered this track to subsequent ripples
2255 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2258 // motion on same track
2259 RegionMoveDrag::motion(event, first_move);
2263 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2264 prev_position = where;
2266 // selection encompasses multiple tracks - just drag
2267 // cross-track drags are forbidden
2268 RegionMoveDrag::motion(event, first_move);
2271 if (!_x_constrained) {
2272 prev_amount += amount;
2275 _last_frame_position = after;
2279 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2281 if (!movement_occurred) {
2285 if (was_double_click() && !_views.empty()) {
2286 DraggingView dv = _views.front();
2287 dv.view->show_region_editor ();
2294 _editor->begin_reversible_command(_("Ripple drag"));
2296 // remove the regions being rippled from the dragging view, updating them to
2297 // their new positions
2298 remove_unselected_from_views (prev_amount, true);
2300 if (allow_moves_across_tracks) {
2302 // if regions were dragged across tracks, we've rippled any later
2303 // regions on the track the regions were dragged off, so we need
2304 // to add the original track to the undo record
2305 orig_tav->playlist()->clear_changes();
2306 vector<Command*> cmds;
2307 orig_tav->playlist()->rdiff (cmds);
2308 _editor->session()->add_commands (cmds);
2310 if (prev_tav && prev_tav != orig_tav) {
2311 prev_tav->playlist()->clear_changes();
2312 vector<Command*> cmds;
2313 prev_tav->playlist()->rdiff (cmds);
2314 _editor->session()->add_commands (cmds);
2317 // selection spanned multiple tracks - all will need adding to undo record
2319 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2320 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2322 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2323 (*pi)->clear_changes();
2324 vector<Command*> cmds;
2325 (*pi)->rdiff (cmds);
2326 _editor->session()->add_commands (cmds);
2330 // other modified playlists are added to undo by RegionMoveDrag::finished()
2331 RegionMoveDrag::finished (event, movement_occurred);
2332 _editor->commit_reversible_command();
2336 RegionRippleDrag::aborted (bool movement_occurred)
2338 RegionMoveDrag::aborted (movement_occurred);
2343 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2345 _view (dynamic_cast<MidiTimeAxisView*> (v))
2347 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2353 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2356 _editor->begin_reversible_command (_("create region"));
2357 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2358 _view->playlist()->freeze ();
2361 framepos_t const f = adjusted_current_frame (event);
2362 if (f < grab_frame()) {
2363 _region->set_initial_position (f);
2366 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2367 so that if this region is duplicated, its duplicate starts on
2368 a snap point rather than 1 frame after a snap point. Otherwise things get
2369 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2370 place snapped notes at the start of the region.
2373 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2374 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2380 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2382 if (!movement_occurred) {
2383 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2385 _view->playlist()->thaw ();
2386 _editor->commit_reversible_command();
2391 RegionCreateDrag::aborted (bool)
2394 _view->playlist()->thaw ();
2400 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2405 , _was_selected (false)
2408 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2412 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2414 Gdk::Cursor* cursor;
2415 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2417 float x_fraction = cnote->mouse_x_fraction ();
2419 if (x_fraction > 0.0 && x_fraction < 0.25) {
2420 cursor = _editor->cursors()->left_side_trim;
2423 cursor = _editor->cursors()->right_side_trim;
2427 Drag::start_grab (event, cursor);
2429 region = &cnote->region_view();
2432 temp = region->snap_to_pixel (cnote->x0 (), true);
2433 _snap_delta = temp - cnote->x0 ();
2437 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2442 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2443 if (ms.size() > 1) {
2444 /* has to be relative, may make no sense otherwise */
2448 if (!(_was_selected = cnote->selected())) {
2450 /* tertiary-click means extend selection - we'll do that on button release,
2451 so don't add it here, because otherwise we make it hard to figure
2452 out the "extend-to" range.
2455 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2458 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2461 region->note_selected (cnote, true);
2463 _editor->get_selection().clear_points();
2464 region->unique_select (cnote);
2471 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2473 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2475 _editor->begin_reversible_command (_("resize notes"));
2477 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2478 MidiRegionSelection::iterator next;
2481 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2483 mrv->begin_resizing (at_front);
2489 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2490 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2492 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2496 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2498 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2499 if (_editor->snap_mode () != SnapOff) {
2503 if (_editor->snap_mode () == SnapOff) {
2505 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2506 if (apply_snap_delta) {
2512 if (apply_snap_delta) {
2516 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2522 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2524 if (!movement_occurred) {
2525 /* no motion - select note */
2526 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2527 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2528 _editor->current_mouse_mode() == Editing::MouseDraw) {
2530 bool changed = false;
2532 if (_was_selected) {
2533 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2535 region->note_deselected (cnote);
2538 _editor->get_selection().clear_points();
2539 region->unique_select (cnote);
2543 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2544 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2546 if (!extend && !add && region->selection_size() > 1) {
2547 _editor->get_selection().clear_points();
2548 region->unique_select (cnote);
2550 } else if (extend) {
2551 region->note_selected (cnote, true, true);
2554 /* it was added during button press */
2560 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2561 _editor->commit_reversible_selection_op();
2568 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2569 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2570 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2572 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2575 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2577 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2578 if (_editor->snap_mode () != SnapOff) {
2582 if (_editor->snap_mode () == SnapOff) {
2584 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2585 if (apply_snap_delta) {
2591 if (apply_snap_delta) {
2595 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2599 _editor->commit_reversible_command ();
2603 NoteResizeDrag::aborted (bool)
2605 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2606 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2607 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2609 mrv->abort_resizing ();
2614 AVDraggingView::AVDraggingView (RegionView* v)
2617 initial_position = v->region()->position ();
2620 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2623 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2626 TrackViewList empty;
2628 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2629 std::list<RegionView*> views = rs.by_layer();
2632 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2633 RegionView* rv = (*i);
2634 if (!rv->region()->video_locked()) {
2637 if (rv->region()->locked()) {
2640 _views.push_back (AVDraggingView (rv));
2645 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2647 Drag::start_grab (event);
2648 if (_editor->session() == 0) {
2652 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2658 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2662 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2663 _max_backwards_drag = (
2664 ARDOUR_UI::instance()->video_timeline->get_duration()
2665 + ARDOUR_UI::instance()->video_timeline->get_offset()
2666 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2669 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2670 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2671 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2674 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2677 Timecode::Time timecode;
2678 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2679 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);
2680 show_verbose_cursor_text (buf);
2684 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2686 if (_editor->session() == 0) {
2689 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2693 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2697 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2698 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2700 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2701 dt = - _max_backwards_drag;
2704 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2705 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2707 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2708 RegionView* rv = i->view;
2709 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2712 rv->region()->clear_changes ();
2713 rv->region()->suspend_property_changes();
2715 rv->region()->set_position(i->initial_position + dt);
2716 rv->region_changed(ARDOUR::Properties::position);
2719 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2720 Timecode::Time timecode;
2721 Timecode::Time timediff;
2723 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2724 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2725 snprintf (buf, sizeof (buf),
2726 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2727 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2728 , _("Video Start:"),
2729 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2731 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2733 show_verbose_cursor_text (buf);
2737 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2739 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2746 if (!movement_occurred || ! _editor->session()) {
2750 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2752 _editor->begin_reversible_command (_("Move Video"));
2754 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2755 ARDOUR_UI::instance()->video_timeline->save_undo();
2756 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2757 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2759 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2760 i->view->drag_end();
2761 i->view->region()->resume_property_changes ();
2763 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2766 _editor->session()->maybe_update_session_range(
2767 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2768 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2772 _editor->commit_reversible_command ();
2776 VideoTimeLineDrag::aborted (bool)
2778 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2781 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2782 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2784 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2785 i->view->region()->resume_property_changes ();
2786 i->view->region()->set_position(i->initial_position);
2790 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2791 : RegionDrag (e, i, p, v)
2792 , _operation (StartTrim)
2793 , _preserve_fade_anchor (preserve_fade_anchor)
2794 , _jump_position_when_done (false)
2796 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2800 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2803 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2804 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2806 if (tv && tv->is_track()) {
2807 speed = tv->track()->speed();
2810 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2811 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2812 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2814 framepos_t const pf = adjusted_current_frame (event);
2815 setup_snap_delta (region_start);
2817 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2818 /* Move the contents of the region around without changing the region bounds */
2819 _operation = ContentsTrim;
2820 Drag::start_grab (event, _editor->cursors()->trimmer);
2822 /* These will get overridden for a point trim.*/
2823 if (pf < (region_start + region_length/2)) {
2824 /* closer to front */
2825 _operation = StartTrim;
2826 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2827 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2829 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2833 _operation = EndTrim;
2834 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2835 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2837 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2841 /* jump trim disabled for now
2842 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2843 _jump_position_when_done = true;
2847 switch (_operation) {
2849 show_verbose_cursor_time (region_start);
2852 show_verbose_cursor_duration (region_start, region_end);
2855 show_verbose_cursor_time (pf);
2859 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2860 i->view->region()->suspend_property_changes ();
2865 TrimDrag::motion (GdkEvent* event, bool first_move)
2867 RegionView* rv = _primary;
2870 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2871 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2872 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2873 frameoffset_t frame_delta = 0;
2875 if (tv && tv->is_track()) {
2876 speed = tv->track()->speed();
2878 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2879 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2885 switch (_operation) {
2887 trim_type = "Region start trim";
2890 trim_type = "Region end trim";
2893 trim_type = "Region content trim";
2900 _editor->begin_reversible_command (trim_type);
2902 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2903 RegionView* rv = i->view;
2904 rv->region()->playlist()->clear_owned_changes ();
2906 if (_operation == StartTrim) {
2907 rv->trim_front_starting ();
2910 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2913 arv->temporarily_hide_envelope ();
2917 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2918 insert_result = _editor->motion_frozen_playlists.insert (pl);
2920 if (insert_result.second) {
2926 bool non_overlap_trim = false;
2928 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2929 non_overlap_trim = true;
2932 /* contstrain trim to fade length */
2933 if (_preserve_fade_anchor) {
2934 switch (_operation) {
2936 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2937 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2939 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2940 if (ar->locked()) continue;
2941 framecnt_t len = ar->fade_in()->back()->when;
2942 if (len < dt) dt = min(dt, len);
2946 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2947 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2949 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2950 if (ar->locked()) continue;
2951 framecnt_t len = ar->fade_out()->back()->when;
2952 if (len < -dt) dt = max(dt, -len);
2960 int32_t divisions = current_music_divisor (adj_frame, event->button.state);
2962 switch (_operation) {
2964 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2965 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2968 if (changed && _preserve_fade_anchor) {
2969 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2971 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2972 framecnt_t len = ar->fade_in()->back()->when;
2973 framecnt_t diff = ar->first_frame() - i->initial_position;
2974 framepos_t new_length = len - diff;
2975 i->anchored_fade_length = min (ar->length(), new_length);
2976 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2977 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2984 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2985 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, divisions);
2986 if (changed && _preserve_fade_anchor) {
2987 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2989 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2990 framecnt_t len = ar->fade_out()->back()->when;
2991 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2992 framepos_t new_length = len + diff;
2993 i->anchored_fade_length = min (ar->length(), new_length);
2994 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2995 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3003 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3005 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3006 i->view->move_contents (frame_delta);
3012 switch (_operation) {
3014 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3017 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3020 // show_verbose_cursor_time (frame_delta);
3026 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3028 if (movement_occurred) {
3029 motion (event, false);
3031 if (_operation == StartTrim) {
3032 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3034 /* This must happen before the region's StatefulDiffCommand is created, as it may
3035 `correct' (ahem) the region's _start from being negative to being zero. It
3036 needs to be zero in the undo record.
3038 i->view->trim_front_ending ();
3040 if (_preserve_fade_anchor) {
3041 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3043 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3044 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3045 ar->set_fade_in_length(i->anchored_fade_length);
3046 ar->set_fade_in_active(true);
3049 if (_jump_position_when_done) {
3050 i->view->region()->set_position (i->initial_position);
3053 } else if (_operation == EndTrim) {
3054 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3055 if (_preserve_fade_anchor) {
3056 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3058 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3059 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3060 ar->set_fade_out_length(i->anchored_fade_length);
3061 ar->set_fade_out_active(true);
3064 if (_jump_position_when_done) {
3065 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3070 if (!_views.empty()) {
3071 if (_operation == StartTrim) {
3072 _editor->maybe_locate_with_edit_preroll(
3073 _views.begin()->view->region()->position());
3075 if (_operation == EndTrim) {
3076 _editor->maybe_locate_with_edit_preroll(
3077 _views.begin()->view->region()->position() +
3078 _views.begin()->view->region()->length());
3082 if (!_editor->selection->selected (_primary)) {
3083 _primary->thaw_after_trim ();
3086 set<boost::shared_ptr<Playlist> > diffed_playlists;
3088 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3089 i->view->thaw_after_trim ();
3090 i->view->enable_display (true);
3092 /* Trimming one region may affect others on the playlist, so we need
3093 to get undo Commands from the whole playlist rather than just the
3094 region. Use diffed_playlists to make sure we don't diff a given
3095 playlist more than once.
3097 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3098 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3099 vector<Command*> cmds;
3101 _editor->session()->add_commands (cmds);
3102 diffed_playlists.insert (p);
3107 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3111 _editor->motion_frozen_playlists.clear ();
3112 _editor->commit_reversible_command();
3115 /* no mouse movement */
3116 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3117 _editor->point_trim (event, adjusted_current_frame (event));
3121 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3122 i->view->region()->resume_property_changes ();
3127 TrimDrag::aborted (bool movement_occurred)
3129 /* Our motion method is changing model state, so use the Undo system
3130 to cancel. Perhaps not ideal, as this will leave an Undo point
3131 behind which may be slightly odd from the user's point of view.
3135 finished (&ev, true);
3137 if (movement_occurred) {
3138 _editor->session()->undo (1);
3141 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3142 i->view->region()->resume_property_changes ();
3147 TrimDrag::setup_pointer_frame_offset ()
3149 list<DraggingView>::iterator i = _views.begin ();
3150 while (i != _views.end() && i->view != _primary) {
3154 if (i == _views.end()) {
3158 switch (_operation) {
3160 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3163 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3170 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3173 , _old_snap_type (e->snap_type())
3174 , _old_snap_mode (e->snap_mode())
3177 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3178 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3180 _real_section = &_marker->meter();
3185 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3187 Drag::start_grab (event, cursor);
3188 show_verbose_cursor_time (adjusted_current_frame(event));
3192 MeterMarkerDrag::setup_pointer_frame_offset ()
3194 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3198 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3201 // create a dummy marker to catch events, then hide it.
3204 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3206 _marker = new MeterMarker (
3208 *_editor->meter_group,
3209 UIConfiguration::instance().color ("meter marker"),
3211 *new MeterSection (_marker->meter())
3214 /* use the new marker for the grab */
3215 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3218 TempoMap& map (_editor->session()->tempo_map());
3219 /* get current state */
3220 before_state = &map.get_state();
3223 _editor->begin_reversible_command (_("move meter mark"));
3225 _editor->begin_reversible_command (_("copy meter mark"));
3227 Timecode::BBT_Time bbt = _real_section->bbt();
3229 /* we can't add a meter where one currently exists */
3230 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3235 const double beat = map.beat_at_bbt (bbt);
3236 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3237 , beat, bbt, _real_section->position_lock_style());
3238 if (!_real_section) {
3244 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3245 if (_real_section->position_lock_style() != AudioTime) {
3246 _editor->set_snap_to (SnapToBar);
3247 _editor->set_snap_mode (SnapNormal);
3251 framepos_t pf = adjusted_current_frame (event);
3253 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3254 /* never snap to music for audio locked */
3255 pf = adjusted_current_frame (event, false);
3258 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3260 /* fake marker meeds to stay under the mouse, unlike the real one. */
3261 _marker->set_position (adjusted_current_frame (event, false));
3263 show_verbose_cursor_time (_real_section->frame());
3267 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3269 if (!movement_occurred) {
3270 if (was_double_click()) {
3271 _editor->edit_meter_marker (*_marker);
3276 /* reinstate old snap setting */
3277 _editor->set_snap_to (_old_snap_type);
3278 _editor->set_snap_mode (_old_snap_mode);
3280 TempoMap& map (_editor->session()->tempo_map());
3282 XMLNode &after = map.get_state();
3283 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3284 _editor->commit_reversible_command ();
3286 // delete the dummy marker we used for visual representation while moving.
3287 // a new visual marker will show up automatically.
3292 MeterMarkerDrag::aborted (bool moved)
3294 _marker->set_position (_marker->meter().frame ());
3296 /* reinstate old snap setting */
3297 _editor->set_snap_to (_old_snap_type);
3298 _editor->set_snap_mode (_old_snap_mode);
3300 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3301 // delete the dummy marker we used for visual representation while moving.
3302 // a new visual marker will show up automatically.
3307 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3313 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3315 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3316 _real_section = &_marker->tempo();
3317 _movable = _real_section->movable();
3318 _grab_bpm = _real_section->note_types_per_minute();
3323 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3325 Drag::start_grab (event, cursor);
3326 if (!_real_section->active()) {
3327 show_verbose_cursor_text (_("inactive"));
3329 show_verbose_cursor_time (adjusted_current_frame (event));
3334 TempoMarkerDrag::setup_pointer_frame_offset ()
3336 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3340 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3342 if (!_real_section->active()) {
3348 // mvc drag - create a dummy marker to catch events, hide it.
3351 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3353 TempoSection section (_marker->tempo());
3355 _marker = new TempoMarker (
3357 *_editor->tempo_group,
3358 UIConfiguration::instance().color ("tempo marker"),
3360 *new TempoSection (_marker->tempo())
3363 /* use the new marker for the grab */
3364 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3367 TempoMap& map (_editor->session()->tempo_map());
3368 /* get current state */
3369 before_state = &map.get_state();
3372 _editor->begin_reversible_command (_("move tempo mark"));
3375 const Tempo tempo (_marker->tempo());
3376 const framepos_t frame = adjusted_current_frame (event) + 1;
3377 const TempoSection::Type type = _real_section->type();
3379 _editor->begin_reversible_command (_("copy tempo mark"));
3381 if (_real_section->position_lock_style() == MusicTime) {
3382 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3383 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3385 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3388 if (!_real_section) {
3396 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3397 /* use vertical movement to alter tempo .. should be log */
3398 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3400 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3402 show_verbose_cursor_text (strs.str());
3404 } else if (_movable && !_real_section->locked_to_meter()) {
3407 if (_editor->snap_musical()) {
3408 /* we can't snap to a grid that we are about to move.
3409 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3411 pf = adjusted_current_frame (event, false);
3413 pf = adjusted_current_frame (event);
3416 TempoMap& map (_editor->session()->tempo_map());
3418 /* snap to beat is 1, snap to bar is -1 (sorry) */
3419 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3421 map.gui_move_tempo (_real_section, pf, sub_num);
3423 show_verbose_cursor_time (_real_section->frame());
3425 _marker->set_position (adjusted_current_frame (event, false));
3429 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3431 if (!_real_section->active()) {
3434 if (!movement_occurred) {
3435 if (was_double_click()) {
3436 _editor->edit_tempo_marker (*_marker);
3441 TempoMap& map (_editor->session()->tempo_map());
3443 XMLNode &after = map.get_state();
3444 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3445 _editor->commit_reversible_command ();
3447 // delete the dummy marker we used for visual representation while moving.
3448 // a new visual marker will show up automatically.
3453 TempoMarkerDrag::aborted (bool moved)
3455 _marker->set_position (_marker->tempo().frame());
3457 TempoMap& map (_editor->session()->tempo_map());
3458 map.set_state (*before_state, Stateful::current_state_version);
3459 // delete the dummy (hidden) marker we used for events while moving.
3464 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3470 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3475 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3477 Drag::start_grab (event, cursor);
3478 TempoMap& map (_editor->session()->tempo_map());
3479 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3482 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3483 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3484 show_verbose_cursor_text (sstr.str());
3485 finished (event, false);
3489 BBTRulerDrag::setup_pointer_frame_offset ()
3491 TempoMap& map (_editor->session()->tempo_map());
3492 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3493 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3496 if (divisions > 0) {
3497 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3499 /* while it makes some sense for the user to determine the division to 'grab',
3500 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3501 and the result over steep tempo curves. Use sixteenths.
3503 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3506 _grab_qn = map.quarter_note_at_beat (beat);
3508 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3513 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3515 TempoMap& map (_editor->session()->tempo_map());
3518 /* get current state */
3519 before_state = &map.get_state();
3520 _editor->begin_reversible_command (_("dilate tempo"));
3525 if (_editor->snap_musical()) {
3526 pf = adjusted_current_frame (event, false);
3528 pf = adjusted_current_frame (event);
3531 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3532 /* adjust previous tempo to match pointer frame */
3533 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3536 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3537 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3538 show_verbose_cursor_text (sstr.str());
3542 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3544 if (!movement_occurred) {
3548 TempoMap& map (_editor->session()->tempo_map());
3550 XMLNode &after = map.get_state();
3551 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3552 _editor->commit_reversible_command ();
3556 BBTRulerDrag::aborted (bool moved)
3559 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3564 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3565 : Drag (e, &c.track_canvas_item(), false)
3570 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3573 /** Do all the things we do when dragging the playhead to make it look as though
3574 * we have located, without actually doing the locate (because that would cause
3575 * the diskstream buffers to be refilled, which is too slow).
3578 CursorDrag::fake_locate (framepos_t t)
3580 if (_editor->session () == 0) {
3584 _editor->playhead_cursor->set_position (t);
3586 Session* s = _editor->session ();
3587 if (s->timecode_transmission_suspended ()) {
3588 framepos_t const f = _editor->playhead_cursor->current_frame ();
3589 /* This is asynchronous so it will be sent "now"
3591 s->send_mmc_locate (f);
3592 /* These are synchronous and will be sent during the next
3595 s->queue_full_time_code ();
3596 s->queue_song_position_pointer ();
3599 show_verbose_cursor_time (t);
3600 _editor->UpdateAllTransportClocks (t);
3604 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3606 Drag::start_grab (event, c);
3607 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3609 _grab_zoom = _editor->samples_per_pixel;
3611 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3613 _editor->snap_to_with_modifier (where, event);
3615 _editor->_dragging_playhead = true;
3617 Session* s = _editor->session ();
3619 /* grab the track canvas item as well */
3621 _cursor.track_canvas_item().grab();
3624 if (_was_rolling && _stop) {
3628 if (s->is_auditioning()) {
3629 s->cancel_audition ();
3633 if (AudioEngine::instance()->connected()) {
3635 /* do this only if we're the engine is connected
3636 * because otherwise this request will never be
3637 * serviced and we'll busy wait forever. likewise,
3638 * notice if we are disconnected while waiting for the
3639 * request to be serviced.
3642 s->request_suspend_timecode_transmission ();
3643 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3644 /* twiddle our thumbs */
3649 fake_locate (where - snap_delta (event->button.state));
3653 CursorDrag::motion (GdkEvent* event, bool)
3655 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3656 _editor->snap_to_with_modifier (where, event);
3657 if (where != last_pointer_frame()) {
3658 fake_locate (where - snap_delta (event->button.state));
3663 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3665 _editor->_dragging_playhead = false;
3667 _cursor.track_canvas_item().ungrab();
3669 if (!movement_occurred && _stop) {
3673 motion (event, false);
3675 Session* s = _editor->session ();
3677 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3678 _editor->_pending_locate_request = true;
3679 s->request_resume_timecode_transmission ();
3684 CursorDrag::aborted (bool)
3686 _cursor.track_canvas_item().ungrab();
3688 if (_editor->_dragging_playhead) {
3689 _editor->session()->request_resume_timecode_transmission ();
3690 _editor->_dragging_playhead = false;
3693 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3696 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3697 : RegionDrag (e, i, p, v)
3699 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3703 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3705 Drag::start_grab (event, cursor);
3707 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3708 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3709 setup_snap_delta (r->position ());
3711 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3715 FadeInDrag::setup_pointer_frame_offset ()
3717 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3718 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3719 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3723 FadeInDrag::motion (GdkEvent* event, bool)
3725 framecnt_t fade_length;
3727 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3728 _editor->snap_to_with_modifier (pos, event);
3729 pos -= snap_delta (event->button.state);
3731 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3733 if (pos < (region->position() + 64)) {
3734 fade_length = 64; // this should be a minimum defined somewhere
3735 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3736 fade_length = region->length() - region->fade_out()->back()->when - 1;
3738 fade_length = pos - region->position();
3741 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3743 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3749 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3752 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3756 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3758 if (!movement_occurred) {
3762 framecnt_t fade_length;
3763 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3764 _editor->snap_to_with_modifier (pos, event);
3765 pos -= snap_delta (event->button.state);
3767 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3769 if (pos < (region->position() + 64)) {
3770 fade_length = 64; // this should be a minimum defined somewhere
3771 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3772 fade_length = region->length() - region->fade_out()->back()->when - 1;
3774 fade_length = pos - region->position();
3777 bool in_command = false;
3779 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3781 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3787 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3788 XMLNode &before = alist->get_state();
3790 tmp->audio_region()->set_fade_in_length (fade_length);
3791 tmp->audio_region()->set_fade_in_active (true);
3794 _editor->begin_reversible_command (_("change fade in length"));
3797 XMLNode &after = alist->get_state();
3798 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3802 _editor->commit_reversible_command ();
3807 FadeInDrag::aborted (bool)
3809 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3810 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3816 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3820 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3821 : RegionDrag (e, i, p, v)
3823 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3827 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3829 Drag::start_grab (event, cursor);
3831 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3832 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3833 setup_snap_delta (r->last_frame ());
3835 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3839 FadeOutDrag::setup_pointer_frame_offset ()
3841 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3842 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3843 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3847 FadeOutDrag::motion (GdkEvent* event, bool)
3849 framecnt_t fade_length;
3851 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3852 _editor->snap_to_with_modifier (pos, event);
3853 pos -= snap_delta (event->button.state);
3855 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3857 if (pos > (region->last_frame() - 64)) {
3858 fade_length = 64; // this should really be a minimum fade defined somewhere
3859 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3860 fade_length = region->length() - region->fade_in()->back()->when - 1;
3862 fade_length = region->last_frame() - pos;
3865 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3867 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3873 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3876 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3880 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3882 if (!movement_occurred) {
3886 framecnt_t fade_length;
3888 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3889 _editor->snap_to_with_modifier (pos, event);
3890 pos -= snap_delta (event->button.state);
3892 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3894 if (pos > (region->last_frame() - 64)) {
3895 fade_length = 64; // this should really be a minimum fade defined somewhere
3896 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3897 fade_length = region->length() - region->fade_in()->back()->when - 1;
3899 fade_length = region->last_frame() - pos;
3902 bool in_command = false;
3904 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3906 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3912 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3913 XMLNode &before = alist->get_state();
3915 tmp->audio_region()->set_fade_out_length (fade_length);
3916 tmp->audio_region()->set_fade_out_active (true);
3919 _editor->begin_reversible_command (_("change fade out length"));
3922 XMLNode &after = alist->get_state();
3923 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3927 _editor->commit_reversible_command ();
3932 FadeOutDrag::aborted (bool)
3934 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3935 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3941 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3945 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3947 , _selection_changed (false)
3949 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3950 Gtk::Window* toplevel = _editor->current_toplevel();
3951 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3955 _points.push_back (ArdourCanvas::Duple (0, 0));
3957 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3960 MarkerDrag::~MarkerDrag ()
3962 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3967 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3969 location = new Location (*l);
3970 markers.push_back (m);
3975 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3977 Drag::start_grab (event, cursor);
3981 Location *location = _editor->find_location_from_marker (_marker, is_start);
3982 _editor->_dragging_edit_point = true;
3984 update_item (location);
3986 // _drag_line->show();
3987 // _line->raise_to_top();
3990 show_verbose_cursor_time (location->start());
3992 show_verbose_cursor_time (location->end());
3994 setup_snap_delta (is_start ? location->start() : location->end());
3996 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3999 case Selection::Toggle:
4000 /* we toggle on the button release */
4002 case Selection::Set:
4003 if (!_editor->selection->selected (_marker)) {
4004 _editor->selection->set (_marker);
4005 _selection_changed = true;
4008 case Selection::Extend:
4010 Locations::LocationList ll;
4011 list<ArdourMarker*> to_add;
4013 _editor->selection->markers.range (s, e);
4014 s = min (_marker->position(), s);
4015 e = max (_marker->position(), e);
4018 if (e < max_framepos) {
4021 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4022 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4023 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4026 to_add.push_back (lm->start);
4029 to_add.push_back (lm->end);
4033 if (!to_add.empty()) {
4034 _editor->selection->add (to_add);
4035 _selection_changed = true;
4039 case Selection::Add:
4040 _editor->selection->add (_marker);
4041 _selection_changed = true;
4046 /* Set up copies for us to manipulate during the drag
4049 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4051 Location* l = _editor->find_location_from_marker (*i, is_start);
4058 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4060 /* range: check that the other end of the range isn't
4063 CopiedLocationInfo::iterator x;
4064 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4065 if (*(*x).location == *l) {
4069 if (x == _copied_locations.end()) {
4070 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4072 (*x).markers.push_back (*i);
4073 (*x).move_both = true;
4081 MarkerDrag::setup_pointer_frame_offset ()
4084 Location *location = _editor->find_location_from_marker (_marker, is_start);
4085 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4089 MarkerDrag::motion (GdkEvent* event, bool)
4091 framecnt_t f_delta = 0;
4093 bool move_both = false;
4094 Location *real_location;
4095 Location *copy_location = 0;
4096 framecnt_t const sd = snap_delta (event->button.state);
4098 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4099 framepos_t next = newframe;
4101 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4105 CopiedLocationInfo::iterator x;
4107 /* find the marker we're dragging, and compute the delta */
4109 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4111 copy_location = (*x).location;
4113 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4115 /* this marker is represented by this
4116 * CopiedLocationMarkerInfo
4119 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4124 if (real_location->is_mark()) {
4125 f_delta = newframe - copy_location->start();
4129 switch (_marker->type()) {
4130 case ArdourMarker::SessionStart:
4131 case ArdourMarker::RangeStart:
4132 case ArdourMarker::LoopStart:
4133 case ArdourMarker::PunchIn:
4134 f_delta = newframe - copy_location->start();
4137 case ArdourMarker::SessionEnd:
4138 case ArdourMarker::RangeEnd:
4139 case ArdourMarker::LoopEnd:
4140 case ArdourMarker::PunchOut:
4141 f_delta = newframe - copy_location->end();
4144 /* what kind of marker is this ? */
4153 if (x == _copied_locations.end()) {
4154 /* hmm, impossible - we didn't find the dragged marker */
4158 /* now move them all */
4160 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4162 copy_location = x->location;
4164 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4168 if (real_location->locked()) {
4172 if (copy_location->is_mark()) {
4176 copy_location->set_start (copy_location->start() + f_delta);
4180 framepos_t new_start = copy_location->start() + f_delta;
4181 framepos_t new_end = copy_location->end() + f_delta;
4183 if (is_start) { // start-of-range marker
4185 if (move_both || (*x).move_both) {
4186 copy_location->set_start (new_start);
4187 copy_location->set_end (new_end);
4188 } else if (new_start < copy_location->end()) {
4189 copy_location->set_start (new_start);
4190 } else if (newframe > 0) {
4191 //_editor->snap_to (next, RoundUpAlways, true);
4192 copy_location->set_end (next);
4193 copy_location->set_start (newframe);
4196 } else { // end marker
4198 if (move_both || (*x).move_both) {
4199 copy_location->set_end (new_end);
4200 copy_location->set_start (new_start);
4201 } else if (new_end > copy_location->start()) {
4202 copy_location->set_end (new_end);
4203 } else if (newframe > 0) {
4204 //_editor->snap_to (next, RoundDownAlways, true);
4205 copy_location->set_start (next);
4206 copy_location->set_end (newframe);
4211 update_item (copy_location);
4213 /* now lookup the actual GUI items used to display this
4214 * location and move them to wherever the copy of the location
4215 * is now. This means that the logic in ARDOUR::Location is
4216 * still enforced, even though we are not (yet) modifying
4217 * the real Location itself.
4220 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4223 lm->set_position (copy_location->start(), copy_location->end());
4228 assert (!_copied_locations.empty());
4230 show_verbose_cursor_time (newframe);
4234 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4236 if (!movement_occurred) {
4238 if (was_double_click()) {
4239 _editor->rename_marker (_marker);
4243 /* just a click, do nothing but finish
4244 off the selection process
4247 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4249 case Selection::Set:
4250 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4251 _editor->selection->set (_marker);
4252 _selection_changed = true;
4256 case Selection::Toggle:
4257 /* we toggle on the button release, click only */
4258 _editor->selection->toggle (_marker);
4259 _selection_changed = true;
4263 case Selection::Extend:
4264 case Selection::Add:
4268 if (_selection_changed) {
4269 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4270 _editor->commit_reversible_selection_op();
4276 _editor->_dragging_edit_point = false;
4278 XMLNode &before = _editor->session()->locations()->get_state();
4279 bool in_command = false;
4281 MarkerSelection::iterator i;
4282 CopiedLocationInfo::iterator x;
4285 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4286 x != _copied_locations.end() && i != _editor->selection->markers.end();
4289 Location * location = _editor->find_location_from_marker (*i, is_start);
4293 if (location->locked()) {
4297 _editor->begin_reversible_command ( _("move marker") );
4300 if (location->is_mark()) {
4301 location->set_start (((*x).location)->start());
4303 location->set (((*x).location)->start(), ((*x).location)->end());
4306 if (location->is_session_range()) {
4307 _editor->session()->set_end_is_free (false);
4313 XMLNode &after = _editor->session()->locations()->get_state();
4314 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4315 _editor->commit_reversible_command ();
4320 MarkerDrag::aborted (bool movement_occurred)
4322 if (!movement_occurred) {
4326 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4328 /* move all markers to their original location */
4331 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4334 Location * location = _editor->find_location_from_marker (*m, is_start);
4337 (*m)->set_position (is_start ? location->start() : location->end());
4344 MarkerDrag::update_item (Location*)
4349 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4351 , _fixed_grab_x (0.0)
4352 , _fixed_grab_y (0.0)
4353 , _cumulative_x_drag (0.0)
4354 , _cumulative_y_drag (0.0)
4358 if (_zero_gain_fraction < 0.0) {
4359 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4362 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4364 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4370 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4372 Drag::start_grab (event, _editor->cursors()->fader);
4374 // start the grab at the center of the control point so
4375 // the point doesn't 'jump' to the mouse after the first drag
4376 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4377 _fixed_grab_y = _point->get_y();
4379 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4380 setup_snap_delta (pos);
4382 float const fraction = 1 - (_point->get_y() / _point->line().height());
4383 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4385 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4387 if (!_point->can_slide ()) {
4388 _x_constrained = true;
4393 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4395 double dx = _drags->current_pointer_x() - last_pointer_x();
4396 double dy = current_pointer_y() - last_pointer_y();
4397 bool need_snap = true;
4399 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4405 /* coordinate in pixels relative to the start of the region (for region-based automation)
4406 or track (for track-based automation) */
4407 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4408 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4410 // calculate zero crossing point. back off by .01 to stay on the
4411 // positive side of zero
4412 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4414 if (_x_constrained) {
4417 if (_y_constrained) {
4421 _cumulative_x_drag = cx - _fixed_grab_x;
4422 _cumulative_y_drag = cy - _fixed_grab_y;
4426 cy = min ((double) _point->line().height(), cy);
4428 // make sure we hit zero when passing through
4429 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4433 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4434 if (!_x_constrained && need_snap) {
4435 _editor->snap_to_with_modifier (cx_frames, event);
4438 cx_frames -= snap_delta (event->button.state);
4439 cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4441 float const fraction = 1.0 - (cy / _point->line().height());
4444 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4445 _editor->begin_reversible_command (_("automation event move"));
4446 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4448 pair<double, float> result;
4449 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4451 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4455 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4457 if (!movement_occurred) {
4460 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4461 _editor->reset_point_selection ();
4465 _point->line().end_drag (_pushing, _final_index);
4466 _editor->commit_reversible_command ();
4471 ControlPointDrag::aborted (bool)
4473 _point->line().reset ();
4477 ControlPointDrag::active (Editing::MouseMode m)
4479 if (m == Editing::MouseDraw) {
4480 /* always active in mouse draw */
4484 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4485 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4488 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4491 , _fixed_grab_x (0.0)
4492 , _fixed_grab_y (0.0)
4493 , _cumulative_y_drag (0)
4497 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4501 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4503 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4506 _item = &_line->grab_item ();
4508 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4509 origin, and ditto for y.
4512 double mx = event->button.x;
4513 double my = event->button.y;
4515 _line->grab_item().canvas_to_item (mx, my);
4517 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4519 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4520 /* no adjacent points */
4524 Drag::start_grab (event, _editor->cursors()->fader);
4526 /* store grab start in item frame */
4527 double const bx = _line->nth (_before)->get_x();
4528 double const ax = _line->nth (_after)->get_x();
4529 double const click_ratio = (ax - mx) / (ax - bx);
4531 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4536 double fraction = 1.0 - (cy / _line->height());
4538 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4542 LineDrag::motion (GdkEvent* event, bool first_move)
4544 double dy = current_pointer_y() - last_pointer_y();
4546 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4550 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4552 _cumulative_y_drag = cy - _fixed_grab_y;
4555 cy = min ((double) _line->height(), cy);
4557 double const fraction = 1.0 - (cy / _line->height());
4561 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4563 _editor->begin_reversible_command (_("automation range move"));
4564 _line->start_drag_line (_before, _after, initial_fraction);
4567 /* we are ignoring x position for this drag, so we can just pass in anything */
4568 pair<double, float> result;
4570 result = _line->drag_motion (0, fraction, true, false, ignored);
4571 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4575 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4577 if (movement_occurred) {
4578 motion (event, false);
4579 _line->end_drag (false, 0);
4580 _editor->commit_reversible_command ();
4582 /* add a new control point on the line */
4584 AutomationTimeAxisView* atv;
4586 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4587 framepos_t where = grab_frame ();
4590 double cy = _fixed_grab_y;
4592 _line->grab_item().item_to_canvas (cx, cy);
4594 atv->add_automation_event (event, where, cy, false);
4595 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4596 AudioRegionView* arv;
4598 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4599 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4606 LineDrag::aborted (bool)
4611 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4615 _region_view_grab_x (0.0),
4616 _cumulative_x_drag (0),
4620 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4624 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4626 Drag::start_grab (event);
4628 _line = reinterpret_cast<Line*> (_item);
4631 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4633 double cx = event->button.x;
4634 double cy = event->button.y;
4636 _item->parent()->canvas_to_item (cx, cy);
4638 /* store grab start in parent frame */
4639 _region_view_grab_x = cx;
4641 _before = *(float*) _item->get_data ("position");
4643 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4645 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4649 FeatureLineDrag::motion (GdkEvent*, bool)
4651 double dx = _drags->current_pointer_x() - last_pointer_x();
4653 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4655 _cumulative_x_drag += dx;
4657 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4666 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4668 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4670 float *pos = new float;
4673 _line->set_data ("position", pos);
4679 FeatureLineDrag::finished (GdkEvent*, bool)
4681 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4682 _arv->update_transient(_before, _before);
4686 FeatureLineDrag::aborted (bool)
4691 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4693 , _vertical_only (false)
4695 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4699 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4701 Drag::start_grab (event);
4702 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4706 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4713 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4715 framepos_t grab = grab_frame ();
4716 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4717 _editor->snap_to_with_modifier (grab, event);
4719 grab = raw_grab_frame ();
4722 /* base start and end on initial click position */
4732 if (current_pointer_y() < grab_y()) {
4733 y1 = current_pointer_y();
4736 y2 = current_pointer_y();
4740 if (start != end || y1 != y2) {
4742 double x1 = _editor->sample_to_pixel (start);
4743 double x2 = _editor->sample_to_pixel (end);
4744 const double min_dimension = 2.0;
4746 if (_vertical_only) {
4747 /* fixed 10 pixel width */
4751 x2 = min (x1 - min_dimension, x2);
4753 x2 = max (x1 + min_dimension, x2);
4758 y2 = min (y1 - min_dimension, y2);
4760 y2 = max (y1 + min_dimension, y2);
4763 /* translate rect into item space and set */
4765 ArdourCanvas::Rect r (x1, y1, x2, y2);
4767 /* this drag is a _trackview_only == true drag, so the y1 and
4768 * y2 (computed using current_pointer_y() and grab_y()) will be
4769 * relative to the top of the trackview group). The
4770 * rubberband rect has the same parent/scroll offset as the
4771 * the trackview group, so we can use the "r" rect directly
4772 * to set the shape of the rubberband.
4775 _editor->rubberband_rect->set (r);
4776 _editor->rubberband_rect->show();
4777 _editor->rubberband_rect->raise_to_top();
4779 show_verbose_cursor_time (pf);
4781 do_select_things (event, true);
4786 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4790 framepos_t grab = grab_frame ();
4791 framepos_t lpf = last_pointer_frame ();
4793 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4794 grab = raw_grab_frame ();
4795 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4809 if (current_pointer_y() < grab_y()) {
4810 y1 = current_pointer_y();
4813 y2 = current_pointer_y();
4817 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4821 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4823 if (movement_occurred) {
4825 motion (event, false);
4826 do_select_things (event, false);
4832 bool do_deselect = true;
4833 MidiTimeAxisView* mtv;
4835 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4837 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4838 /* nothing selected */
4839 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4840 do_deselect = false;
4844 /* do not deselect if Primary or Tertiary (toggle-select or
4845 * extend-select are pressed.
4848 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4849 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4856 _editor->rubberband_rect->hide();
4860 RubberbandSelectDrag::aborted (bool)
4862 _editor->rubberband_rect->hide ();
4865 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4866 : RegionDrag (e, i, p, v)
4868 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4872 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4874 Drag::start_grab (event, cursor);
4876 _editor->get_selection().add (_primary);
4878 framepos_t where = _primary->region()->position();
4879 setup_snap_delta (where);
4881 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4885 TimeFXDrag::motion (GdkEvent* event, bool)
4887 RegionView* rv = _primary;
4888 StreamView* cv = rv->get_time_axis_view().view ();
4890 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4891 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4892 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4893 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4894 _editor->snap_to_with_modifier (pf, event);
4895 pf -= snap_delta (event->button.state);
4897 if (pf > rv->region()->position()) {
4898 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4901 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4905 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4907 /* this may have been a single click, no drag. We still want the dialog
4908 to show up in that case, so that the user can manually edit the
4909 parameters for the timestretch.
4912 float fraction = 1.0;
4914 if (movement_occurred) {
4916 motion (event, false);
4918 _primary->get_time_axis_view().hide_timestretch ();
4920 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4922 if (adjusted_frame_pos < _primary->region()->position()) {
4923 /* backwards drag of the left edge - not usable */
4927 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4929 fraction = (double) newlen / (double) _primary->region()->length();
4931 #ifndef USE_RUBBERBAND
4932 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4933 if (_primary->region()->data_type() == DataType::AUDIO) {
4934 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4939 if (!_editor->get_selection().regions.empty()) {
4940 /* primary will already be included in the selection, and edit
4941 group shared editing will propagate selection across
4942 equivalent regions, so just use the current region
4946 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4947 error << _("An error occurred while executing time stretch operation") << endmsg;
4953 TimeFXDrag::aborted (bool)
4955 _primary->get_time_axis_view().hide_timestretch ();
4958 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4961 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4965 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4967 Drag::start_grab (event);
4971 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4973 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4977 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4979 if (movement_occurred && _editor->session()) {
4980 /* make sure we stop */
4981 _editor->session()->request_transport_speed (0.0);
4986 ScrubDrag::aborted (bool)
4991 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4995 , _time_selection_at_start (!_editor->get_selection().time.empty())
4997 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4999 if (_time_selection_at_start) {
5000 start_at_start = _editor->get_selection().time.start();
5001 end_at_start = _editor->get_selection().time.end_frame();
5006 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5008 if (_editor->session() == 0) {
5012 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5014 switch (_operation) {
5015 case CreateSelection:
5016 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5021 cursor = _editor->cursors()->selector;
5022 Drag::start_grab (event, cursor);
5025 case SelectionStartTrim:
5026 if (_editor->clicked_axisview) {
5027 _editor->clicked_axisview->order_selection_trims (_item, true);
5029 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5032 case SelectionEndTrim:
5033 if (_editor->clicked_axisview) {
5034 _editor->clicked_axisview->order_selection_trims (_item, false);
5036 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5040 Drag::start_grab (event, cursor);
5043 case SelectionExtend:
5044 Drag::start_grab (event, cursor);
5048 if (_operation == SelectionMove) {
5049 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5051 show_verbose_cursor_time (adjusted_current_frame (event));
5056 SelectionDrag::setup_pointer_frame_offset ()
5058 switch (_operation) {
5059 case CreateSelection:
5060 _pointer_frame_offset = 0;
5063 case SelectionStartTrim:
5065 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5068 case SelectionEndTrim:
5069 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5072 case SelectionExtend:
5078 SelectionDrag::motion (GdkEvent* event, bool first_move)
5080 framepos_t start = 0;
5082 framecnt_t length = 0;
5083 framecnt_t distance = 0;
5085 framepos_t const pending_position = adjusted_current_frame (event);
5087 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5091 switch (_operation) {
5092 case CreateSelection:
5094 framepos_t grab = grab_frame ();
5097 grab = adjusted_current_frame (event, false);
5098 if (grab < pending_position) {
5099 _editor->snap_to (grab, RoundDownMaybe);
5101 _editor->snap_to (grab, RoundUpMaybe);
5105 if (pending_position < grab) {
5106 start = pending_position;
5109 end = pending_position;
5113 /* first drag: Either add to the selection
5114 or create a new selection
5121 /* adding to the selection */
5122 _editor->set_selected_track_as_side_effect (Selection::Add);
5123 _editor->clicked_selection = _editor->selection->add (start, end);
5130 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5131 _editor->set_selected_track_as_side_effect (Selection::Set);
5134 _editor->clicked_selection = _editor->selection->set (start, end);
5138 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5139 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5140 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5142 _editor->selection->add (atest);
5146 /* select all tracks within the rectangle that we've marked out so far */
5147 TrackViewList new_selection;
5148 TrackViewList& all_tracks (_editor->track_views);
5150 ArdourCanvas::Coord const top = grab_y();
5151 ArdourCanvas::Coord const bottom = current_pointer_y();
5153 if (top >= 0 && bottom >= 0) {
5155 //first, find the tracks that are covered in the y range selection
5156 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5157 if ((*i)->covered_by_y_range (top, bottom)) {
5158 new_selection.push_back (*i);
5162 //now find any tracks that are GROUPED with the tracks we selected
5163 TrackViewList grouped_add = new_selection;
5164 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5165 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5166 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5167 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5168 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5169 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5170 grouped_add.push_back (*j);
5175 //now compare our list with the current selection, and add or remove as necessary
5176 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5177 TrackViewList tracks_to_add;
5178 TrackViewList tracks_to_remove;
5179 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5180 if ( !_editor->selection->tracks.contains ( *i ) )
5181 tracks_to_add.push_back ( *i );
5182 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5183 if ( !grouped_add.contains ( *i ) )
5184 tracks_to_remove.push_back ( *i );
5185 _editor->selection->add(tracks_to_add);
5186 _editor->selection->remove(tracks_to_remove);
5192 case SelectionStartTrim:
5194 end = _editor->selection->time[_editor->clicked_selection].end;
5196 if (pending_position > end) {
5199 start = pending_position;
5203 case SelectionEndTrim:
5205 start = _editor->selection->time[_editor->clicked_selection].start;
5207 if (pending_position < start) {
5210 end = pending_position;
5217 start = _editor->selection->time[_editor->clicked_selection].start;
5218 end = _editor->selection->time[_editor->clicked_selection].end;
5220 length = end - start;
5221 distance = pending_position - start;
5222 start = pending_position;
5223 _editor->snap_to (start);
5225 end = start + length;
5229 case SelectionExtend:
5234 switch (_operation) {
5236 if (_time_selection_at_start) {
5237 _editor->selection->move_time (distance);
5241 _editor->selection->replace (_editor->clicked_selection, start, end);
5245 if (_operation == SelectionMove) {
5246 show_verbose_cursor_time(start);
5248 show_verbose_cursor_time(pending_position);
5253 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5255 Session* s = _editor->session();
5257 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5258 if (movement_occurred) {
5259 motion (event, false);
5260 /* XXX this is not object-oriented programming at all. ick */
5261 if (_editor->selection->time.consolidate()) {
5262 _editor->selection->TimeChanged ();
5265 /* XXX what if its a music time selection? */
5267 if (s->get_play_range() && s->transport_rolling()) {
5268 s->request_play_range (&_editor->selection->time, true);
5269 } else if (!s->config.get_external_sync()) {
5270 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5271 if (_operation == SelectionEndTrim)
5272 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5274 s->request_locate (_editor->get_selection().time.start());
5278 if (_editor->get_selection().time.length() != 0) {
5279 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5281 s->clear_range_selection ();
5286 /* just a click, no pointer movement.
5289 if (_operation == SelectionExtend) {
5290 if (_time_selection_at_start) {
5291 framepos_t pos = adjusted_current_frame (event, false);
5292 framepos_t start = min (pos, start_at_start);
5293 framepos_t end = max (pos, end_at_start);
5294 _editor->selection->set (start, end);
5297 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5298 if (_editor->clicked_selection) {
5299 _editor->selection->remove (_editor->clicked_selection);
5302 if (!_editor->clicked_selection) {
5303 _editor->selection->clear_time();
5308 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5309 _editor->selection->set (_editor->clicked_axisview);
5312 if (s && s->get_play_range () && s->transport_rolling()) {
5313 s->request_stop (false, false);
5318 _editor->stop_canvas_autoscroll ();
5319 _editor->clicked_selection = 0;
5320 _editor->commit_reversible_selection_op ();
5324 SelectionDrag::aborted (bool)
5329 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5330 : Drag (e, i, false),
5334 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5336 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5337 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5338 physical_screen_height (_editor->current_toplevel()->get_window())));
5339 _drag_rect->hide ();
5341 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5342 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5345 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5347 /* normal canvas items will be cleaned up when their parent group is deleted. But
5348 this item is created as the child of a long-lived parent group, and so we
5349 need to explicitly delete it.
5355 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5357 if (_editor->session() == 0) {
5361 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5363 if (!_editor->temp_location) {
5364 _editor->temp_location = new Location (*_editor->session());
5367 switch (_operation) {
5368 case CreateSkipMarker:
5369 case CreateRangeMarker:
5370 case CreateTransportMarker:
5371 case CreateCDMarker:
5373 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5378 cursor = _editor->cursors()->selector;
5382 Drag::start_grab (event, cursor);
5384 show_verbose_cursor_time (adjusted_current_frame (event));
5388 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5390 framepos_t start = 0;
5392 ArdourCanvas::Rectangle *crect;
5394 switch (_operation) {
5395 case CreateSkipMarker:
5396 crect = _editor->range_bar_drag_rect;
5398 case CreateRangeMarker:
5399 crect = _editor->range_bar_drag_rect;
5401 case CreateTransportMarker:
5402 crect = _editor->transport_bar_drag_rect;
5404 case CreateCDMarker:
5405 crect = _editor->cd_marker_bar_drag_rect;
5408 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5413 framepos_t const pf = adjusted_current_frame (event);
5415 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5416 framepos_t grab = grab_frame ();
5417 _editor->snap_to (grab);
5419 if (pf < grab_frame()) {
5427 /* first drag: Either add to the selection
5428 or create a new selection.
5433 _editor->temp_location->set (start, end);
5437 update_item (_editor->temp_location);
5439 //_drag_rect->raise_to_top();
5445 _editor->temp_location->set (start, end);
5447 double x1 = _editor->sample_to_pixel (start);
5448 double x2 = _editor->sample_to_pixel (end);
5452 update_item (_editor->temp_location);
5455 show_verbose_cursor_time (pf);
5460 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5462 Location * newloc = 0;
5466 if (movement_occurred) {
5467 motion (event, false);
5470 switch (_operation) {
5471 case CreateSkipMarker:
5472 case CreateRangeMarker:
5473 case CreateCDMarker:
5475 XMLNode &before = _editor->session()->locations()->get_state();
5476 if (_operation == CreateSkipMarker) {
5477 _editor->begin_reversible_command (_("new skip marker"));
5478 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5479 flags = Location::IsRangeMarker | Location::IsSkip;
5480 _editor->range_bar_drag_rect->hide();
5481 } else if (_operation == CreateCDMarker) {
5482 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5483 _editor->begin_reversible_command (_("new CD marker"));
5484 flags = Location::IsRangeMarker | Location::IsCDMarker;
5485 _editor->cd_marker_bar_drag_rect->hide();
5487 _editor->begin_reversible_command (_("new skip marker"));
5488 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5489 flags = Location::IsRangeMarker;
5490 _editor->range_bar_drag_rect->hide();
5492 newloc = new Location (
5493 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5496 _editor->session()->locations()->add (newloc, true);
5497 XMLNode &after = _editor->session()->locations()->get_state();
5498 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5499 _editor->commit_reversible_command ();
5503 case CreateTransportMarker:
5504 // popup menu to pick loop or punch
5505 _editor->new_transport_marker_context_menu (&event->button, _item);
5511 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5513 if (_operation == CreateTransportMarker) {
5515 /* didn't drag, so just locate */
5517 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5519 } else if (_operation == CreateCDMarker) {
5521 /* didn't drag, but mark is already created so do
5524 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5529 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5531 if (end == max_framepos) {
5532 end = _editor->session()->current_end_frame ();
5535 if (start == max_framepos) {
5536 start = _editor->session()->current_start_frame ();
5539 switch (_editor->mouse_mode) {
5541 /* find the two markers on either side and then make the selection from it */
5542 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5546 /* find the two markers on either side of the click and make the range out of it */
5547 _editor->selection->set (start, end);
5556 _editor->stop_canvas_autoscroll ();
5560 RangeMarkerBarDrag::aborted (bool movement_occurred)
5562 if (movement_occurred) {
5563 _drag_rect->hide ();
5568 RangeMarkerBarDrag::update_item (Location* location)
5570 double const x1 = _editor->sample_to_pixel (location->start());
5571 double const x2 = _editor->sample_to_pixel (location->end());
5573 _drag_rect->set_x0 (x1);
5574 _drag_rect->set_x1 (x2);
5577 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5579 , _cumulative_dx (0)
5580 , _cumulative_dy (0)
5581 , _was_selected (false)
5583 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5585 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5587 _region = &_primary->region_view ();
5588 _note_height = _region->midi_stream_view()->note_height ();
5592 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5594 Drag::start_grab (event);
5595 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5597 if (!(_was_selected = _primary->selected())) {
5599 /* tertiary-click means extend selection - we'll do that on button release,
5600 so don't add it here, because otherwise we make it hard to figure
5601 out the "extend-to" range.
5604 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5607 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5610 _region->note_selected (_primary, true);
5612 _editor->get_selection().clear_points();
5613 _region->unique_select (_primary);
5619 /** @return Current total drag x change in frames */
5621 NoteDrag::total_dx (const guint state) const
5623 if (_x_constrained) {
5626 TempoMap& map (_editor->session()->tempo_map());
5629 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5631 /* primary note time */
5632 double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5633 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5635 /* new time of the primary note in session frames */
5636 frameoffset_t st = n + dx + snap_delta (state);
5638 framepos_t const rp = _region->region()->position ();
5640 /* prevent the note being dragged earlier than the region's position */
5643 /* possibly snap and return corresponding delta */
5647 if (ArdourKeyboard::indicates_snap (state)) {
5648 if (_editor->snap_mode () != SnapOff) {
5652 if (_editor->snap_mode () == SnapOff) {
5654 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5655 if (ArdourKeyboard::indicates_snap_delta (state)) {
5663 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5664 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5666 ret = st - n - snap_delta (state);
5671 /** @return Current total drag y change in note number */
5673 NoteDrag::total_dy () const
5675 if (_y_constrained) {
5679 double const y = _region->midi_view()->y_position ();
5680 /* new current note */
5681 uint8_t n = _region->y_to_note (current_pointer_y () - y);
5683 MidiStreamView* msv = _region->midi_stream_view ();
5684 n = max (msv->lowest_note(), n);
5685 n = min (msv->highest_note(), n);
5686 /* and work out delta */
5687 return n - _region->y_to_note (grab_y() - y);
5691 NoteDrag::motion (GdkEvent * event, bool)
5693 /* Total change in x and y since the start of the drag */
5694 frameoffset_t const dx = total_dx (event->button.state);
5695 int8_t const dy = total_dy ();
5697 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5698 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5699 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5702 _cumulative_dx += tdx;
5703 _cumulative_dy += tdy;
5705 int8_t note_delta = total_dy();
5708 _region->move_selection (tdx, tdy, note_delta);
5710 /* the new note value may be the same as the old one, but we
5711 * don't know what that means because the selection may have
5712 * involved more than one note and we might be doing something
5713 * odd with them. so show the note value anyway, always.
5716 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5718 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5724 NoteDrag::finished (GdkEvent* ev, bool moved)
5727 /* no motion - select note */
5729 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5730 _editor->current_mouse_mode() == Editing::MouseDraw) {
5732 bool changed = false;
5734 if (_was_selected) {
5735 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5737 _region->note_deselected (_primary);
5740 _editor->get_selection().clear_points();
5741 _region->unique_select (_primary);
5745 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5746 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5748 if (!extend && !add && _region->selection_size() > 1) {
5749 _editor->get_selection().clear_points();
5750 _region->unique_select (_primary);
5752 } else if (extend) {
5753 _region->note_selected (_primary, true, true);
5756 /* it was added during button press */
5763 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5764 _editor->commit_reversible_selection_op();
5768 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5773 NoteDrag::aborted (bool)
5778 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5779 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5780 : Drag (editor, atv->base_item ())
5782 , _y_origin (atv->y_position())
5783 , _nothing_to_drag (false)
5785 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5786 setup (atv->lines ());
5789 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5790 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5791 : Drag (editor, rv->get_canvas_group ())
5793 , _y_origin (rv->get_time_axis_view().y_position())
5794 , _nothing_to_drag (false)
5797 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5799 list<boost::shared_ptr<AutomationLine> > lines;
5801 AudioRegionView* audio_view;
5802 AutomationRegionView* automation_view;
5803 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5804 lines.push_back (audio_view->get_gain_line ());
5805 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5806 lines.push_back (automation_view->line ());
5809 error << _("Automation range drag created for invalid region type") << endmsg;
5815 /** @param lines AutomationLines to drag.
5816 * @param offset Offset from the session start to the points in the AutomationLines.
5819 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5821 /* find the lines that overlap the ranges being dragged */
5822 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5823 while (i != lines.end ()) {
5824 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5827 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5829 /* check this range against all the AudioRanges that we are using */
5830 list<AudioRange>::const_iterator k = _ranges.begin ();
5831 while (k != _ranges.end()) {
5832 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5838 /* add it to our list if it overlaps at all */
5839 if (k != _ranges.end()) {
5844 _lines.push_back (n);
5850 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5854 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5856 return 1.0 - ((global_y - _y_origin) / line->height());
5860 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5862 const double v = list->eval(x);
5863 return _integral ? rint(v) : v;
5867 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5869 Drag::start_grab (event, cursor);
5871 /* Get line states before we start changing things */
5872 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5873 i->state = &i->line->get_state ();
5874 i->original_fraction = y_fraction (i->line, current_pointer_y());
5877 if (_ranges.empty()) {
5879 /* No selected time ranges: drag all points */
5880 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5881 uint32_t const N = i->line->npoints ();
5882 for (uint32_t j = 0; j < N; ++j) {
5883 i->points.push_back (i->line->nth (j));
5889 if (_nothing_to_drag) {
5895 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5897 if (_nothing_to_drag && !first_move) {
5902 _editor->begin_reversible_command (_("automation range move"));
5904 if (!_ranges.empty()) {
5906 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5908 framecnt_t const half = (i->start + i->end) / 2;
5910 /* find the line that this audio range starts in */
5911 list<Line>::iterator j = _lines.begin();
5912 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5916 if (j != _lines.end()) {
5917 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5919 /* j is the line that this audio range starts in; fade into it;
5920 64 samples length plucked out of thin air.
5923 framepos_t a = i->start + 64;
5928 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5929 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5931 XMLNode &before = the_list->get_state();
5932 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5933 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5935 if (add_p || add_q) {
5936 _editor->session()->add_command (
5937 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5941 /* same thing for the end */
5944 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5948 if (j != _lines.end()) {
5949 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5951 /* j is the line that this audio range starts in; fade out of it;
5952 64 samples length plucked out of thin air.
5955 framepos_t b = i->end - 64;
5960 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5961 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5963 XMLNode &before = the_list->get_state();
5964 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5965 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5967 if (add_p || add_q) {
5968 _editor->session()->add_command (
5969 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5974 _nothing_to_drag = true;
5976 /* Find all the points that should be dragged and put them in the relevant
5977 points lists in the Line structs.
5980 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5982 uint32_t const N = i->line->npoints ();
5983 for (uint32_t j = 0; j < N; ++j) {
5985 /* here's a control point on this line */
5986 ControlPoint* p = i->line->nth (j);
5987 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5989 /* see if it's inside a range */
5990 list<AudioRange>::const_iterator k = _ranges.begin ();
5991 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5995 if (k != _ranges.end()) {
5996 /* dragging this point */
5997 _nothing_to_drag = false;
5998 i->points.push_back (p);
6004 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6005 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6009 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6010 float const f = y_fraction (l->line, current_pointer_y());
6011 /* we are ignoring x position for this drag, so we can just pass in anything */
6012 pair<double, float> result;
6014 result = l->line->drag_motion (0, f, true, false, ignored);
6015 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6020 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6022 if (_nothing_to_drag || !motion_occurred) {
6026 motion (event, false);
6027 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6028 i->line->end_drag (false, 0);
6031 _editor->commit_reversible_command ();
6035 AutomationRangeDrag::aborted (bool)
6037 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6042 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6044 , initial_time_axis_view (itav)
6046 /* note that time_axis_view may be null if the regionview was created
6047 * as part of a copy operation.
6049 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6050 layer = v->region()->layer ();
6051 initial_y = v->get_canvas_group()->position().y;
6052 initial_playlist = v->region()->playlist ();
6053 initial_position = v->region()->position ();
6054 initial_end = v->region()->position () + v->region()->length ();
6057 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6058 : Drag (e, i->canvas_item ())
6061 , _cumulative_dx (0)
6063 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6064 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6069 PatchChangeDrag::motion (GdkEvent* ev, bool)
6071 framepos_t f = adjusted_current_frame (ev);
6072 boost::shared_ptr<Region> r = _region_view->region ();
6073 f = max (f, r->position ());
6074 f = min (f, r->last_frame ());
6076 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6077 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6078 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6079 _cumulative_dx = dxu;
6083 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6085 if (!movement_occurred) {
6086 if (was_double_click()) {
6087 _region_view->edit_patch_change (_patch_change);
6092 boost::shared_ptr<Region> r (_region_view->region ());
6093 framepos_t f = adjusted_current_frame (ev);
6094 f = max (f, r->position ());
6095 f = min (f, r->last_frame ());
6097 _region_view->move_patch_change (
6099 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6104 PatchChangeDrag::aborted (bool)
6106 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6110 PatchChangeDrag::setup_pointer_frame_offset ()
6112 boost::shared_ptr<Region> region = _region_view->region ();
6113 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6116 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6117 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6124 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6126 _region_view->update_drag_selection (
6128 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6132 MidiRubberbandSelectDrag::deselect_things ()
6137 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6138 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6141 _vertical_only = true;
6145 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6147 double const y = _region_view->midi_view()->y_position ();
6149 y1 = max (0.0, y1 - y);
6150 y2 = max (0.0, y2 - y);
6152 _region_view->update_vertical_drag_selection (
6155 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6160 MidiVerticalSelectDrag::deselect_things ()
6165 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6166 : RubberbandSelectDrag (e, i)
6172 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6174 if (drag_in_progress) {
6175 /* We just want to select things at the end of the drag, not during it */
6179 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6181 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6183 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6185 _editor->commit_reversible_selection_op ();
6189 EditorRubberbandSelectDrag::deselect_things ()
6191 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6193 _editor->selection->clear_tracks();
6194 _editor->selection->clear_regions();
6195 _editor->selection->clear_points ();
6196 _editor->selection->clear_lines ();
6197 _editor->selection->clear_midi_notes ();
6199 _editor->commit_reversible_selection_op();
6202 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6207 _note[0] = _note[1] = 0;
6210 NoteCreateDrag::~NoteCreateDrag ()
6216 NoteCreateDrag::grid_frames (framepos_t t) const
6219 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6220 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6222 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6223 - _region_view->region_beats_to_region_frames (t_beats);
6227 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6229 Drag::start_grab (event, cursor);
6231 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6232 TempoMap& map (_editor->session()->tempo_map());
6234 const framepos_t pf = _drags->current_pointer_frame ();
6235 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6237 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6239 double eqaf = map.exact_qn_at_frame (pf, divisions);
6241 if (divisions != 0) {
6243 const double qaf = map.quarter_note_at_frame (pf);
6245 /* Hack so that we always snap to the note that we are over, instead of snapping
6246 to the next one if we're more than halfway through the one we're over.
6249 const double rem = eqaf - qaf;
6251 eqaf -= grid_beats.to_double();
6255 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6256 /* minimum initial length is grid beats */
6257 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6259 double const x0 = _editor->sample_to_pixel (_note[0]);
6260 double const x1 = _editor->sample_to_pixel (_note[1]);
6261 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6263 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6264 _drag_rect->set_outline_all ();
6265 _drag_rect->set_outline_color (0xffffff99);
6266 _drag_rect->set_fill_color (0xffffff66);
6270 NoteCreateDrag::motion (GdkEvent* event, bool)
6272 TempoMap& map (_editor->session()->tempo_map());
6273 const framepos_t pf = _drags->current_pointer_frame ();
6274 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6275 double eqaf = map.exact_qn_at_frame (pf, divisions);
6277 if (divisions != 0) {
6279 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6281 const double qaf = map.quarter_note_at_frame (pf);
6282 /* Hack so that we always snap to the note that we are over, instead of snapping
6283 to the next one if we're more than halfway through the one we're over.
6286 const double rem = eqaf - qaf;
6288 eqaf -= grid_beats.to_double();
6291 eqaf += grid_beats.to_double();
6293 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6295 double const x0 = _editor->sample_to_pixel (_note[0]);
6296 double const x1 = _editor->sample_to_pixel (_note[1]);
6297 _drag_rect->set_x0 (std::min(x0, x1));
6298 _drag_rect->set_x1 (std::max(x0, x1));
6302 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6304 /* we create a note even if there was no movement */
6305 framepos_t const start = min (_note[0], _note[1]);
6306 framepos_t const start_sess_rel = start + _region_view->region()->position();
6307 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6308 framecnt_t const g = grid_frames (start);
6310 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6314 TempoMap& map (_editor->session()->tempo_map());
6315 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6316 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6318 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6322 NoteCreateDrag::y_to_region (double y) const
6325 _region_view->get_canvas_group()->canvas_to_item (x, y);
6330 NoteCreateDrag::aborted (bool)
6335 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6343 HitCreateDrag::~HitCreateDrag ()
6348 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6350 Drag::start_grab (event, cursor);
6352 TempoMap& map (_editor->session()->tempo_map());
6354 const framepos_t pf = _drags->current_pointer_frame ();
6355 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6357 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6359 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6361 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6365 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6366 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6368 Evoral::Beats length = _region_view->get_grid_beats (pf);
6370 _region_view->create_note_at (start, y, length, event->button.state, false);
6377 HitCreateDrag::motion (GdkEvent* event, bool)
6379 TempoMap& map (_editor->session()->tempo_map());
6381 const framepos_t pf = _drags->current_pointer_frame ();
6382 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6384 if (divisions == 0) {
6388 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6389 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6390 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6392 if (_last_pos == start && y == _last_y) {
6396 Evoral::Beats length = _region_view->get_grid_beats (pf);
6398 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6399 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6403 _region_view->create_note_at (start, y, length, event->button.state, false);
6410 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6416 HitCreateDrag::y_to_region (double y) const
6419 _region_view->get_canvas_group()->canvas_to_item (x, y);
6424 HitCreateDrag::aborted (bool)
6429 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6434 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6438 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6440 Drag::start_grab (event, cursor);
6444 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6450 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6453 distance = _drags->current_pointer_x() - grab_x();
6454 len = ar->fade_in()->back()->when;
6456 distance = grab_x() - _drags->current_pointer_x();
6457 len = ar->fade_out()->back()->when;
6460 /* how long should it be ? */
6462 new_length = len + _editor->pixel_to_sample (distance);
6464 /* now check with the region that this is legal */
6466 new_length = ar->verify_xfade_bounds (new_length, start);
6469 arv->reset_fade_in_shape_width (ar, new_length);
6471 arv->reset_fade_out_shape_width (ar, new_length);
6476 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6482 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6485 distance = _drags->current_pointer_x() - grab_x();
6486 len = ar->fade_in()->back()->when;
6488 distance = grab_x() - _drags->current_pointer_x();
6489 len = ar->fade_out()->back()->when;
6492 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6494 _editor->begin_reversible_command ("xfade trim");
6495 ar->playlist()->clear_owned_changes ();
6498 ar->set_fade_in_length (new_length);
6500 ar->set_fade_out_length (new_length);
6503 /* Adjusting the xfade may affect other regions in the playlist, so we need
6504 to get undo Commands from the whole playlist rather than just the
6508 vector<Command*> cmds;
6509 ar->playlist()->rdiff (cmds);
6510 _editor->session()->add_commands (cmds);
6511 _editor->commit_reversible_command ();
6516 CrossfadeEdgeDrag::aborted (bool)
6519 // arv->redraw_start_xfade ();
6521 // arv->redraw_end_xfade ();
6525 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6526 : Drag (e, item, true)
6527 , line (new EditorCursor (*e))
6529 line->set_position (pos);
6531 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6534 RegionCutDrag::~RegionCutDrag ()
6540 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6542 Drag::start_grab (event, c);
6543 motion (event, false);
6547 RegionCutDrag::motion (GdkEvent* event, bool)
6549 framepos_t pos = _drags->current_pointer_frame();
6550 _editor->snap_to_with_modifier (pos, event);
6552 line->set_position (pos);
6556 RegionCutDrag::finished (GdkEvent* event, bool)
6558 _editor->get_track_canvas()->canvas()->re_enter();
6560 framepos_t pos = _drags->current_pointer_frame();
6561 _editor->snap_to_with_modifier (pos, event);
6565 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6571 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state),
6576 RegionCutDrag::aborted (bool)
6580 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6581 : Drag (e, item, true)
6586 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6588 Drag::start_grab (event, c);
6590 framepos_t where = _editor->canvas_event_sample(event);
6592 _editor->_dragging_playhead = true;
6594 _editor->playhead_cursor->set_position (where);
6598 RulerZoomDrag::motion (GdkEvent* event, bool)
6600 framepos_t where = _editor->canvas_event_sample(event);
6602 _editor->playhead_cursor->set_position (where);
6604 const double movement_limit = 20.0;
6605 const double scale = 1.08;
6606 const double y_delta = last_pointer_y() - current_pointer_y();
6608 if (y_delta > 0 && y_delta < movement_limit) {
6609 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6610 } else if (y_delta < 0 && y_delta > -movement_limit) {
6611 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6616 RulerZoomDrag::finished (GdkEvent*, bool)
6618 _editor->_dragging_playhead = false;
6620 Session* s = _editor->session ();
6622 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6623 _editor->_pending_locate_request = true;
6629 RulerZoomDrag::aborted (bool)