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;
1127 /* The TimeAxisView that this region is now over */
1128 TimeAxisView* current_tv = _time_axis_views[track_index];
1130 /* Ensure it is moved from stacked -> expanded if appropriate */
1131 if (current_tv->view()->layer_display() == Stacked) {
1132 current_tv->view()->set_layer_display (Expanded);
1135 /* We're only allowed to go -ve in layer on Expanded views */
1136 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1137 this_delta_layer = - i->layer;
1141 rv->set_height (current_tv->view()->child_height ());
1143 /* Update show/hidden status as the region view may have come from a hidden track,
1144 or have moved to one.
1146 if (current_tv->hidden ()) {
1147 rv->get_canvas_group()->hide ();
1149 rv->get_canvas_group()->show ();
1152 /* Update the DraggingView */
1153 i->time_axis_view = track_index;
1154 i->layer += this_delta_layer;
1157 _editor->mouse_brush_insert_region (rv, pending_region_position);
1161 /* Get the y coordinate of the top of the track that this region is now over */
1162 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1164 /* And adjust for the layer that it should be on */
1165 StreamView* cv = current_tv->view ();
1166 switch (cv->layer_display ()) {
1170 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1173 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1177 /* need to get the parent of the regionview
1178 * canvas group and get its position in
1179 * equivalent coordinate space as the trackview
1180 * we are now dragging over.
1183 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1188 /* Now move the region view */
1189 rv->move (x_delta, y_delta);
1191 } /* foreach region */
1193 _total_x_delta += x_delta;
1195 if (x_delta != 0 && !_brushing) {
1196 show_verbose_cursor_time (_last_frame_position);
1199 /* keep track of pointer movement */
1201 /* the pointer is currently over a time axis view */
1203 if (_last_pointer_time_axis_view < 0) {
1204 /* last motion event was not over a time axis view
1205 * or last y-movement out of the dropzone was not valid
1208 if (delta_time_axis_view < 0) {
1209 /* in the drop zone, moving up */
1211 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1212 * We do not use negative _last_pointer_time_axis_view because
1213 * the dropzone is "packed" (the actual track offset is ignored)
1215 * As opposed to the actual number
1216 * of elements in the dropzone (_ndropzone)
1217 * _pdropzone is not constrained. This is necessary
1218 * to allow moving multiple regions with y-distance
1221 * There can be 0 elements in the dropzone,
1222 * even though the drag-pointer is inside the DZ.
1225 * [ Audio-track, Midi-track, Audio-track, DZ ]
1226 * move regions from both audio tracks at the same time into the
1227 * DZ by grabbing the region in the bottom track.
1229 assert(current_pointer_time_axis_view >= 0);
1230 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1234 /* only move out of the zone if the movement is OK */
1235 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1236 assert(delta_time_axis_view < 0);
1237 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1238 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1239 * the current position can be calculated as follows:
1241 // a well placed oofus attack can still throw this off.
1242 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1243 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1246 /* last motion event was also over a time axis view */
1247 _last_pointer_time_axis_view += delta_time_axis_view;
1248 assert(_last_pointer_time_axis_view >= 0);
1253 /* the pointer is not over a time axis view */
1254 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1255 _pdropzone += delta_time_axis_view - delta_skip;
1256 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1259 _last_pointer_layer += delta_layer;
1263 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1265 if (_copy && first_move) {
1266 if (_x_constrained && !_brushing) {
1267 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1268 } else if (!_brushing) {
1269 _editor->begin_reversible_command (Operations::region_copy);
1270 } else if (_brushing) {
1271 _editor->begin_reversible_command (Operations::drag_region_brush);
1273 /* duplicate the regionview(s) and region(s) */
1275 list<DraggingView> new_regionviews;
1277 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1279 RegionView* rv = i->view;
1280 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1281 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1283 const boost::shared_ptr<const Region> original = rv->region();
1284 boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true
1285 , current_music_divisor (original->position(), event->button.state));
1286 /* need to set this so that the drop zone code can work. This doesn't
1287 actually put the region into the playlist, but just sets a weak pointer
1290 region_copy->set_playlist (original->playlist());
1294 boost::shared_ptr<AudioRegion> audioregion_copy
1295 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1297 nrv = new AudioRegionView (*arv, audioregion_copy);
1299 boost::shared_ptr<MidiRegion> midiregion_copy
1300 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1301 nrv = new MidiRegionView (*mrv, midiregion_copy);
1306 nrv->get_canvas_group()->show ();
1307 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1309 /* swap _primary to the copy */
1311 if (rv == _primary) {
1315 /* ..and deselect the one we copied */
1317 rv->set_selected (false);
1320 if (!new_regionviews.empty()) {
1322 /* reflect the fact that we are dragging the copies */
1324 _views = new_regionviews;
1326 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1329 } else if (!_copy && first_move) {
1330 if (_x_constrained && !_brushing) {
1331 _editor->begin_reversible_command (_("fixed time region drag"));
1332 } else if (!_brushing) {
1333 _editor->begin_reversible_command (Operations::region_drag);
1334 } else if (_brushing) {
1335 _editor->begin_reversible_command (Operations::drag_region_brush);
1338 RegionMotionDrag::motion (event, first_move);
1342 RegionMotionDrag::finished (GdkEvent *, bool)
1344 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1345 if (!(*i)->view()) {
1349 if ((*i)->view()->layer_display() == Expanded) {
1350 (*i)->view()->set_layer_display (Stacked);
1356 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1358 RegionMotionDrag::finished (ev, movement_occurred);
1360 if (!movement_occurred) {
1364 if (was_double_click() && !_views.empty()) {
1365 DraggingView dv = _views.front();
1366 dv.view->show_region_editor ();
1373 assert (!_views.empty ());
1375 /* We might have hidden region views so that they weren't visible during the drag
1376 (when they have been reparented). Now everything can be shown again, as region
1377 views are back in their track parent groups.
1379 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1380 i->view->get_canvas_group()->show ();
1383 bool const changed_position = (_last_frame_position != _primary->region()->position());
1384 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1385 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1407 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1411 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1413 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1418 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1419 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1420 uint32_t output_chan = region->n_channels();
1421 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1422 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1424 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1425 TimeAxisView* tav =_editor->axis_view_from_stripable (audio_tracks.front());
1427 tav->set_height (original->current_height());
1429 return dynamic_cast<RouteTimeAxisView*>(tav);
1431 ChanCount one_midi_port (DataType::MIDI, 1);
1432 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1433 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1434 (ARDOUR::Plugin::PresetRecord*) 0,
1435 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1436 TimeAxisView* tav = _editor->axis_view_from_stripable (midi_tracks.front());
1438 tav->set_height (original->current_height());
1440 return dynamic_cast<RouteTimeAxisView*> (tav);
1443 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1449 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state)
1451 RegionSelection new_views;
1452 PlaylistSet modified_playlists;
1453 RouteTimeAxisView* new_time_axis_view = 0;
1456 /* all changes were made during motion event handlers */
1458 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1462 _editor->commit_reversible_command ();
1466 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1467 PlaylistMapping playlist_mapping;
1469 /* insert the regions into their new playlists */
1470 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1472 RouteTimeAxisView* dest_rtv = 0;
1474 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1480 if (changed_position && !_x_constrained) {
1481 where = i->view->region()->position() - drag_delta;
1483 where = i->view->region()->position();
1486 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1487 /* dragged to drop zone */
1489 PlaylistMapping::iterator pm;
1491 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1492 /* first region from this original playlist: create a new track */
1493 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1494 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1495 dest_rtv = new_time_axis_view;
1497 /* we already created a new track for regions from this playlist, use it */
1498 dest_rtv = pm->second;
1501 /* destination time axis view is the one we dragged to */
1502 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1505 if (dest_rtv != 0) {
1506 RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1507 modified_playlists, current_music_divisor (where, ev_state));
1509 if (new_view != 0) {
1510 new_views.push_back (new_view);
1514 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1515 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1518 list<DraggingView>::const_iterator next = i;
1524 /* If we've created new regions either by copying or moving
1525 to a new track, we want to replace the old selection with the new ones
1528 if (new_views.size() > 0) {
1529 _editor->selection->set (new_views);
1532 /* write commands for the accumulated diffs for all our modified playlists */
1533 add_stateful_diff_commands_for_playlists (modified_playlists);
1535 _editor->commit_reversible_command ();
1539 RegionMoveDrag::finished_no_copy (
1540 bool const changed_position,
1541 bool const changed_tracks,
1542 framecnt_t const drag_delta,
1543 int32_t const ev_state
1546 RegionSelection new_views;
1547 PlaylistSet modified_playlists;
1548 PlaylistSet frozen_playlists;
1549 set<RouteTimeAxisView*> views_to_update;
1550 RouteTimeAxisView* new_time_axis_view = 0;
1552 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1553 PlaylistMapping playlist_mapping;
1555 std::set<boost::shared_ptr<const Region> > uniq;
1556 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1558 RegionView* rv = i->view;
1559 RouteTimeAxisView* dest_rtv = 0;
1561 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1566 if (uniq.find (rv->region()) != uniq.end()) {
1567 /* prevent duplicate moves when selecting regions from shared playlists */
1571 uniq.insert(rv->region());
1573 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1574 /* dragged to drop zone */
1576 PlaylistMapping::iterator pm;
1578 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1579 /* first region from this original playlist: create a new track */
1580 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1581 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1582 dest_rtv = new_time_axis_view;
1584 /* we already created a new track for regions from this playlist, use it */
1585 dest_rtv = pm->second;
1589 /* destination time axis view is the one we dragged to */
1590 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1595 double const dest_layer = i->layer;
1597 views_to_update.insert (dest_rtv);
1601 if (changed_position && !_x_constrained) {
1602 where = rv->region()->position() - drag_delta;
1604 where = rv->region()->position();
1607 if (changed_tracks) {
1609 /* insert into new playlist */
1611 RegionView* new_view = insert_region_into_playlist (
1612 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1613 modified_playlists, current_music_divisor (where, ev_state)
1616 if (new_view == 0) {
1621 new_views.push_back (new_view);
1623 /* remove from old playlist */
1625 /* the region that used to be in the old playlist is not
1626 moved to the new one - we use a copy of it. as a result,
1627 any existing editor for the region should no longer be
1630 rv->hide_region_editor();
1633 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1637 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1639 /* this movement may result in a crossfade being modified, or a layering change,
1640 so we need to get undo data from the playlist as well as the region.
1643 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1645 playlist->clear_changes ();
1648 rv->region()->clear_changes ();
1651 motion on the same track. plonk the previously reparented region
1652 back to its original canvas group (its streamview).
1653 No need to do anything for copies as they are fake regions which will be deleted.
1656 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1657 rv->get_canvas_group()->set_y_position (i->initial_y);
1660 /* just change the model */
1661 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1662 playlist->set_layer (rv->region(), dest_layer);
1665 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1667 r = frozen_playlists.insert (playlist);
1670 playlist->freeze ();
1673 rv->region()->set_position (where, current_music_divisor (where, ev_state));
1674 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1677 if (changed_tracks) {
1679 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1680 was selected in all of them, then removing it from a playlist will have removed all
1681 trace of it from _views (i.e. there were N regions selected, we removed 1,
1682 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1683 corresponding regionview, and _views is now empty).
1685 This could have invalidated any and all iterators into _views.
1687 The heuristic we use here is: if the region selection is empty, break out of the loop
1688 here. if the region selection is not empty, then restart the loop because we know that
1689 we must have removed at least the region(view) we've just been working on as well as any
1690 that we processed on previous iterations.
1692 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1693 we can just iterate.
1697 if (_views.empty()) {
1708 /* If we've created new regions either by copying or moving
1709 to a new track, we want to replace the old selection with the new ones
1712 if (new_views.size() > 0) {
1713 _editor->selection->set (new_views);
1716 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1720 /* write commands for the accumulated diffs for all our modified playlists */
1721 add_stateful_diff_commands_for_playlists (modified_playlists);
1722 /* applies to _brushing */
1723 _editor->commit_reversible_command ();
1725 /* We have futzed with the layering of canvas items on our streamviews.
1726 If any region changed layer, this will have resulted in the stream
1727 views being asked to set up their region views, and all will be well.
1728 If not, we might now have badly-ordered region views. Ask the StreamViews
1729 involved to sort themselves out, just in case.
1732 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1733 (*i)->view()->playlist_layered ((*i)->track ());
1737 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1738 * @param region Region to remove.
1739 * @param playlist playlist To remove from.
1740 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1741 * that clear_changes () is only called once per playlist.
1744 RegionMoveDrag::remove_region_from_playlist (
1745 boost::shared_ptr<Region> region,
1746 boost::shared_ptr<Playlist> playlist,
1747 PlaylistSet& modified_playlists
1750 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1753 playlist->clear_changes ();
1756 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1760 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1761 * clearing the playlist's diff history first if necessary.
1762 * @param region Region to insert.
1763 * @param dest_rtv Destination RouteTimeAxisView.
1764 * @param dest_layer Destination layer.
1765 * @param where Destination position.
1766 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1767 * that clear_changes () is only called once per playlist.
1768 * @return New RegionView, or 0 if no insert was performed.
1771 RegionMoveDrag::insert_region_into_playlist (
1772 boost::shared_ptr<Region> region,
1773 RouteTimeAxisView* dest_rtv,
1776 PlaylistSet& modified_playlists,
1777 const int32_t sub_num
1780 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1781 if (!dest_playlist) {
1785 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1786 _new_region_view = 0;
1787 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1789 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1790 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1792 dest_playlist->clear_changes ();
1794 dest_playlist->add_region (region, where, 1.0, false, sub_num);
1796 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1797 dest_playlist->set_layer (region, dest_layer);
1802 assert (_new_region_view);
1804 return _new_region_view;
1808 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1810 _new_region_view = rv;
1814 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1816 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1817 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1819 _editor->session()->add_command (c);
1828 RegionMoveDrag::aborted (bool movement_occurred)
1832 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1833 list<DraggingView>::const_iterator next = i;
1842 RegionMotionDrag::aborted (movement_occurred);
1847 RegionMotionDrag::aborted (bool)
1849 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1851 StreamView* sview = (*i)->view();
1854 if (sview->layer_display() == Expanded) {
1855 sview->set_layer_display (Stacked);
1860 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1861 RegionView* rv = i->view;
1862 TimeAxisView* tv = &(rv->get_time_axis_view ());
1863 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1865 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1866 rv->get_canvas_group()->set_y_position (0);
1868 rv->move (-_total_x_delta, 0);
1869 rv->set_height (rtv->view()->child_height ());
1873 /** @param b true to brush, otherwise false.
1874 * @param c true to make copies of the regions being moved, otherwise false.
1876 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1877 : RegionMotionDrag (e, i, p, v, b)
1879 , _new_region_view (0)
1881 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1884 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1885 if (rtv && rtv->is_track()) {
1886 speed = rtv->track()->speed ();
1889 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1893 RegionMoveDrag::setup_pointer_frame_offset ()
1895 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1898 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1899 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1901 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1903 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1904 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1906 _primary = v->view()->create_region_view (r, false, false);
1908 _primary->get_canvas_group()->show ();
1909 _primary->set_position (pos, 0);
1910 _views.push_back (DraggingView (_primary, this, v));
1912 _last_frame_position = pos;
1914 _item = _primary->get_canvas_group ();
1918 RegionInsertDrag::finished (GdkEvent * event, bool)
1920 int pos = _views.front().time_axis_view;
1921 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1923 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1925 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1926 _primary->get_canvas_group()->set_y_position (0);
1928 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1930 _editor->begin_reversible_command (Operations::insert_region);
1931 playlist->clear_changes ();
1932 playlist->add_region (_primary->region (), _last_frame_position);
1934 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1935 if (Config->get_edit_mode() == Ripple) {
1936 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1939 _editor->session()->add_command (new StatefulDiffCommand (playlist));
1940 _editor->commit_reversible_command ();
1948 RegionInsertDrag::aborted (bool)
1955 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1956 : RegionMoveDrag (e, i, p, v, false, false)
1958 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1961 struct RegionSelectionByPosition {
1962 bool operator() (RegionView*a, RegionView* b) {
1963 return a->region()->position () < b->region()->position();
1968 RegionSpliceDrag::motion (GdkEvent* event, bool)
1970 /* Which trackview is this ? */
1972 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1973 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1975 /* The region motion is only processed if the pointer is over
1979 if (!tv || !tv->is_track()) {
1980 /* To make sure we hide the verbose canvas cursor when the mouse is
1981 not held over an audio track.
1983 _editor->verbose_cursor()->hide ();
1986 _editor->verbose_cursor()->show ();
1991 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1997 RegionSelection copy;
1998 _editor->selection->regions.by_position(copy);
2000 framepos_t const pf = adjusted_current_frame (event);
2002 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2004 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2010 boost::shared_ptr<Playlist> playlist;
2012 if ((playlist = atv->playlist()) == 0) {
2016 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2021 if (pf < (*i)->region()->last_frame() + 1) {
2025 if (pf > (*i)->region()->first_frame()) {
2031 playlist->shuffle ((*i)->region(), dir);
2036 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2038 RegionMoveDrag::finished (event, movement_occurred);
2042 RegionSpliceDrag::aborted (bool)
2052 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2055 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2057 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2058 RegionSelection to_ripple;
2059 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2060 if ((*i)->position() >= where) {
2061 to_ripple.push_back (rtv->view()->find_view(*i));
2065 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2066 if (!exclude.contains (*i)) {
2067 // the selection has already been added to _views
2069 if (drag_in_progress) {
2070 // do the same things that RegionMotionDrag::motion does when
2071 // first_move is true, for the region views that we're adding
2072 // to _views this time
2075 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2076 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2077 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2078 rvg->reparent (_editor->_drag_motion_group);
2080 // we only need to move in the y direction
2081 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2086 _views.push_back (DraggingView (*i, this, tav));
2092 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2095 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2096 // we added all the regions after the selection
2098 std::list<DraggingView>::iterator to_erase = i++;
2099 if (!_editor->selection->regions.contains (to_erase->view)) {
2100 // restore the non-selected regions to their original playlist & positions,
2101 // and then ripple them back by the length of the regions that were dragged away
2102 // do the same things as RegionMotionDrag::aborted
2104 RegionView *rv = to_erase->view;
2105 TimeAxisView* tv = &(rv->get_time_axis_view ());
2106 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2109 // plonk them back onto their own track
2110 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2111 rv->get_canvas_group()->set_y_position (0);
2115 // move the underlying region to match the view
2116 rv->region()->set_position (rv->region()->position() + amount);
2118 // restore the view to match the underlying region's original position
2119 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2122 rv->set_height (rtv->view()->child_height ());
2123 _views.erase (to_erase);
2129 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2131 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2133 return allow_moves_across_tracks;
2141 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2142 : RegionMoveDrag (e, i, p, v, false, false)
2144 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2145 // compute length of selection
2146 RegionSelection selected_regions = _editor->selection->regions;
2147 selection_length = selected_regions.end_frame() - selected_regions.start();
2149 // we'll only allow dragging to another track in ripple mode if all the regions
2150 // being dragged start off on the same track
2151 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2154 exclude = new RegionList;
2155 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2156 exclude->push_back((*i)->region());
2159 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2160 RegionSelection copy;
2161 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2163 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2164 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2166 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2167 // find ripple start point on each applicable playlist
2168 RegionView *first_selected_on_this_track = NULL;
2169 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2170 if ((*i)->region()->playlist() == (*pi)) {
2171 // region is on this playlist - it's the first, because they're sorted
2172 first_selected_on_this_track = *i;
2176 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2177 add_all_after_to_views (
2178 &first_selected_on_this_track->get_time_axis_view(),
2179 first_selected_on_this_track->region()->position(),
2180 selected_regions, false);
2183 if (allow_moves_across_tracks) {
2184 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2192 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2194 /* Which trackview is this ? */
2196 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2197 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2199 /* The region motion is only processed if the pointer is over
2203 if (!tv || !tv->is_track()) {
2204 /* To make sure we hide the verbose canvas cursor when the mouse is
2205 not held over an audiotrack.
2207 _editor->verbose_cursor()->hide ();
2211 framepos_t where = adjusted_current_frame (event);
2212 assert (where >= 0);
2214 double delta = compute_x_delta (event, &after);
2216 framecnt_t amount = _editor->pixel_to_sample (delta);
2218 if (allow_moves_across_tracks) {
2219 // all the originally selected regions were on the same track
2221 framecnt_t adjust = 0;
2222 if (prev_tav && tv != prev_tav) {
2223 // dragged onto a different track
2224 // remove the unselected regions from _views, restore them to their original positions
2225 // and add the regions after the drop point on the new playlist to _views instead.
2226 // undo the effect of rippling the previous playlist, and include the effect of removing
2227 // the dragged region(s) from this track
2229 remove_unselected_from_views (prev_amount, false);
2230 // ripple previous playlist according to the regions that have been removed onto the new playlist
2231 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2234 // move just the selected regions
2235 RegionMoveDrag::motion(event, first_move);
2237 // ensure that the ripple operation on the new playlist inserts selection_length time
2238 adjust = selection_length;
2239 // ripple the new current playlist
2240 tv->playlist()->ripple (where, amount+adjust, exclude);
2242 // add regions after point where drag entered this track to subsequent ripples
2243 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2246 // motion on same track
2247 RegionMoveDrag::motion(event, first_move);
2251 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2252 prev_position = where;
2254 // selection encompasses multiple tracks - just drag
2255 // cross-track drags are forbidden
2256 RegionMoveDrag::motion(event, first_move);
2259 if (!_x_constrained) {
2260 prev_amount += amount;
2263 _last_frame_position = after;
2267 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2269 if (!movement_occurred) {
2273 if (was_double_click() && !_views.empty()) {
2274 DraggingView dv = _views.front();
2275 dv.view->show_region_editor ();
2282 _editor->begin_reversible_command(_("Ripple drag"));
2284 // remove the regions being rippled from the dragging view, updating them to
2285 // their new positions
2286 remove_unselected_from_views (prev_amount, true);
2288 if (allow_moves_across_tracks) {
2290 // if regions were dragged across tracks, we've rippled any later
2291 // regions on the track the regions were dragged off, so we need
2292 // to add the original track to the undo record
2293 orig_tav->playlist()->clear_changes();
2294 vector<Command*> cmds;
2295 orig_tav->playlist()->rdiff (cmds);
2296 _editor->session()->add_commands (cmds);
2298 if (prev_tav && prev_tav != orig_tav) {
2299 prev_tav->playlist()->clear_changes();
2300 vector<Command*> cmds;
2301 prev_tav->playlist()->rdiff (cmds);
2302 _editor->session()->add_commands (cmds);
2305 // selection spanned multiple tracks - all will need adding to undo record
2307 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2308 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2310 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2311 (*pi)->clear_changes();
2312 vector<Command*> cmds;
2313 (*pi)->rdiff (cmds);
2314 _editor->session()->add_commands (cmds);
2318 // other modified playlists are added to undo by RegionMoveDrag::finished()
2319 RegionMoveDrag::finished (event, movement_occurred);
2320 _editor->commit_reversible_command();
2324 RegionRippleDrag::aborted (bool movement_occurred)
2326 RegionMoveDrag::aborted (movement_occurred);
2331 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2333 _view (dynamic_cast<MidiTimeAxisView*> (v))
2335 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2341 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2344 _editor->begin_reversible_command (_("create region"));
2345 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2346 _view->playlist()->freeze ();
2349 framepos_t const f = adjusted_current_frame (event);
2350 if (f < grab_frame()) {
2351 _region->set_initial_position (f);
2354 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2355 so that if this region is duplicated, its duplicate starts on
2356 a snap point rather than 1 frame after a snap point. Otherwise things get
2357 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2358 place snapped notes at the start of the region.
2361 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2362 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2368 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2370 if (!movement_occurred) {
2371 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2373 _view->playlist()->thaw ();
2374 _editor->commit_reversible_command();
2379 RegionCreateDrag::aborted (bool)
2382 _view->playlist()->thaw ();
2388 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2393 , _was_selected (false)
2396 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2400 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2402 Gdk::Cursor* cursor;
2403 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2405 float x_fraction = cnote->mouse_x_fraction ();
2407 if (x_fraction > 0.0 && x_fraction < 0.25) {
2408 cursor = _editor->cursors()->left_side_trim;
2411 cursor = _editor->cursors()->right_side_trim;
2415 Drag::start_grab (event, cursor);
2417 region = &cnote->region_view();
2420 temp = region->snap_to_pixel (cnote->x0 (), true);
2421 _snap_delta = temp - cnote->x0 ();
2425 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2430 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2431 if (ms.size() > 1) {
2432 /* has to be relative, may make no sense otherwise */
2436 if (!(_was_selected = cnote->selected())) {
2438 /* tertiary-click means extend selection - we'll do that on button release,
2439 so don't add it here, because otherwise we make it hard to figure
2440 out the "extend-to" range.
2443 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2446 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2449 region->note_selected (cnote, true);
2451 _editor->get_selection().clear_points();
2452 region->unique_select (cnote);
2459 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2461 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2463 _editor->begin_reversible_command (_("resize notes"));
2465 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2466 MidiRegionSelection::iterator next;
2469 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2471 mrv->begin_resizing (at_front);
2477 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2478 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2480 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2484 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2486 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2487 if (_editor->snap_mode () != SnapOff) {
2491 if (_editor->snap_mode () == SnapOff) {
2493 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2494 if (apply_snap_delta) {
2500 if (apply_snap_delta) {
2504 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2510 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2512 if (!movement_occurred) {
2513 /* no motion - select note */
2514 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2515 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2516 _editor->current_mouse_mode() == Editing::MouseDraw) {
2518 bool changed = false;
2520 if (_was_selected) {
2521 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2523 region->note_deselected (cnote);
2526 _editor->get_selection().clear_points();
2527 region->unique_select (cnote);
2531 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2532 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2534 if (!extend && !add && region->selection_size() > 1) {
2535 _editor->get_selection().clear_points();
2536 region->unique_select (cnote);
2538 } else if (extend) {
2539 region->note_selected (cnote, true, true);
2542 /* it was added during button press */
2548 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2549 _editor->commit_reversible_selection_op();
2556 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2557 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2558 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2560 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2563 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2565 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2566 if (_editor->snap_mode () != SnapOff) {
2570 if (_editor->snap_mode () == SnapOff) {
2572 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2573 if (apply_snap_delta) {
2579 if (apply_snap_delta) {
2583 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2587 _editor->commit_reversible_command ();
2591 NoteResizeDrag::aborted (bool)
2593 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2594 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2595 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2597 mrv->abort_resizing ();
2602 AVDraggingView::AVDraggingView (RegionView* v)
2605 initial_position = v->region()->position ();
2608 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2611 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2614 TrackViewList empty;
2616 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2617 std::list<RegionView*> views = rs.by_layer();
2620 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2621 RegionView* rv = (*i);
2622 if (!rv->region()->video_locked()) {
2625 if (rv->region()->locked()) {
2628 _views.push_back (AVDraggingView (rv));
2633 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2635 Drag::start_grab (event);
2636 if (_editor->session() == 0) {
2640 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2646 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2650 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2651 _max_backwards_drag = (
2652 ARDOUR_UI::instance()->video_timeline->get_duration()
2653 + ARDOUR_UI::instance()->video_timeline->get_offset()
2654 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2657 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2658 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2659 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2662 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2665 Timecode::Time timecode;
2666 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2667 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);
2668 show_verbose_cursor_text (buf);
2672 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2674 if (_editor->session() == 0) {
2677 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2681 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2685 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2686 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2688 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2689 dt = - _max_backwards_drag;
2692 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2693 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2695 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2696 RegionView* rv = i->view;
2697 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2700 rv->region()->clear_changes ();
2701 rv->region()->suspend_property_changes();
2703 rv->region()->set_position(i->initial_position + dt);
2704 rv->region_changed(ARDOUR::Properties::position);
2707 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2708 Timecode::Time timecode;
2709 Timecode::Time timediff;
2711 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2712 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2713 snprintf (buf, sizeof (buf),
2714 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2715 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2716 , _("Video Start:"),
2717 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2719 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2721 show_verbose_cursor_text (buf);
2725 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2727 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2734 if (!movement_occurred || ! _editor->session()) {
2738 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2740 _editor->begin_reversible_command (_("Move Video"));
2742 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2743 ARDOUR_UI::instance()->video_timeline->save_undo();
2744 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2745 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2747 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2748 i->view->drag_end();
2749 i->view->region()->resume_property_changes ();
2751 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2754 _editor->session()->maybe_update_session_range(
2755 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2756 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2760 _editor->commit_reversible_command ();
2764 VideoTimeLineDrag::aborted (bool)
2766 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2769 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2770 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2772 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2773 i->view->region()->resume_property_changes ();
2774 i->view->region()->set_position(i->initial_position);
2778 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2779 : RegionDrag (e, i, p, v)
2780 , _operation (StartTrim)
2781 , _preserve_fade_anchor (preserve_fade_anchor)
2782 , _jump_position_when_done (false)
2784 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2788 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2791 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2792 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2794 if (tv && tv->is_track()) {
2795 speed = tv->track()->speed();
2798 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2799 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2800 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2802 framepos_t const pf = adjusted_current_frame (event);
2803 setup_snap_delta (region_start);
2805 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2806 /* Move the contents of the region around without changing the region bounds */
2807 _operation = ContentsTrim;
2808 Drag::start_grab (event, _editor->cursors()->trimmer);
2810 /* These will get overridden for a point trim.*/
2811 if (pf < (region_start + region_length/2)) {
2812 /* closer to front */
2813 _operation = StartTrim;
2814 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2815 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2817 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2821 _operation = EndTrim;
2822 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2823 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2825 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2829 /* jump trim disabled for now
2830 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2831 _jump_position_when_done = true;
2835 switch (_operation) {
2837 show_verbose_cursor_time (region_start);
2840 show_verbose_cursor_duration (region_start, region_end);
2843 show_verbose_cursor_time (pf);
2847 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2848 i->view->region()->suspend_property_changes ();
2853 TrimDrag::motion (GdkEvent* event, bool first_move)
2855 RegionView* rv = _primary;
2858 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2859 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2860 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2861 frameoffset_t frame_delta = 0;
2863 if (tv && tv->is_track()) {
2864 speed = tv->track()->speed();
2866 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2867 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2873 switch (_operation) {
2875 trim_type = "Region start trim";
2878 trim_type = "Region end trim";
2881 trim_type = "Region content trim";
2888 _editor->begin_reversible_command (trim_type);
2890 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2891 RegionView* rv = i->view;
2892 rv->region()->playlist()->clear_owned_changes ();
2894 if (_operation == StartTrim) {
2895 rv->trim_front_starting ();
2898 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2901 arv->temporarily_hide_envelope ();
2905 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2906 insert_result = _editor->motion_frozen_playlists.insert (pl);
2908 if (insert_result.second) {
2914 bool non_overlap_trim = false;
2916 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2917 non_overlap_trim = true;
2920 /* contstrain trim to fade length */
2921 if (_preserve_fade_anchor) {
2922 switch (_operation) {
2924 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2925 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2927 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2928 if (ar->locked()) continue;
2929 framecnt_t len = ar->fade_in()->back()->when;
2930 if (len < dt) dt = min(dt, len);
2934 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2935 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2937 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2938 if (ar->locked()) continue;
2939 framecnt_t len = ar->fade_out()->back()->when;
2940 if (len < -dt) dt = max(dt, -len);
2948 int32_t divisions = current_music_divisor (adj_frame, event->button.state);
2950 switch (_operation) {
2952 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2953 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2956 if (changed && _preserve_fade_anchor) {
2957 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2959 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2960 framecnt_t len = ar->fade_in()->back()->when;
2961 framecnt_t diff = ar->first_frame() - i->initial_position;
2962 framepos_t new_length = len - diff;
2963 i->anchored_fade_length = min (ar->length(), new_length);
2964 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
2965 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2972 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2973 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, divisions);
2974 if (changed && _preserve_fade_anchor) {
2975 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2977 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2978 framecnt_t len = ar->fade_out()->back()->when;
2979 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2980 framepos_t new_length = len + diff;
2981 i->anchored_fade_length = min (ar->length(), new_length);
2982 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
2983 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2991 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2993 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2994 i->view->move_contents (frame_delta);
3000 switch (_operation) {
3002 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3005 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3008 // show_verbose_cursor_time (frame_delta);
3014 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3016 if (movement_occurred) {
3017 motion (event, false);
3019 if (_operation == StartTrim) {
3020 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3022 /* This must happen before the region's StatefulDiffCommand is created, as it may
3023 `correct' (ahem) the region's _start from being negative to being zero. It
3024 needs to be zero in the undo record.
3026 i->view->trim_front_ending ();
3028 if (_preserve_fade_anchor) {
3029 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3031 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3032 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3033 ar->set_fade_in_length(i->anchored_fade_length);
3034 ar->set_fade_in_active(true);
3037 if (_jump_position_when_done) {
3038 i->view->region()->set_position (i->initial_position);
3041 } else if (_operation == EndTrim) {
3042 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3043 if (_preserve_fade_anchor) {
3044 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3046 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3047 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3048 ar->set_fade_out_length(i->anchored_fade_length);
3049 ar->set_fade_out_active(true);
3052 if (_jump_position_when_done) {
3053 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3058 if (!_views.empty()) {
3059 if (_operation == StartTrim) {
3060 _editor->maybe_locate_with_edit_preroll(
3061 _views.begin()->view->region()->position());
3063 if (_operation == EndTrim) {
3064 _editor->maybe_locate_with_edit_preroll(
3065 _views.begin()->view->region()->position() +
3066 _views.begin()->view->region()->length());
3070 if (!_editor->selection->selected (_primary)) {
3071 _primary->thaw_after_trim ();
3074 set<boost::shared_ptr<Playlist> > diffed_playlists;
3076 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3077 i->view->thaw_after_trim ();
3078 i->view->enable_display (true);
3080 /* Trimming one region may affect others on the playlist, so we need
3081 to get undo Commands from the whole playlist rather than just the
3082 region. Use diffed_playlists to make sure we don't diff a given
3083 playlist more than once.
3085 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3086 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3087 vector<Command*> cmds;
3089 _editor->session()->add_commands (cmds);
3090 diffed_playlists.insert (p);
3095 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3099 _editor->motion_frozen_playlists.clear ();
3100 _editor->commit_reversible_command();
3103 /* no mouse movement */
3104 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3105 _editor->point_trim (event, adjusted_current_frame (event));
3109 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3110 i->view->region()->resume_property_changes ();
3115 TrimDrag::aborted (bool movement_occurred)
3117 /* Our motion method is changing model state, so use the Undo system
3118 to cancel. Perhaps not ideal, as this will leave an Undo point
3119 behind which may be slightly odd from the user's point of view.
3123 finished (&ev, true);
3125 if (movement_occurred) {
3126 _editor->session()->undo (1);
3129 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3130 i->view->region()->resume_property_changes ();
3135 TrimDrag::setup_pointer_frame_offset ()
3137 list<DraggingView>::iterator i = _views.begin ();
3138 while (i != _views.end() && i->view != _primary) {
3142 if (i == _views.end()) {
3146 switch (_operation) {
3148 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3151 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3158 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3161 , _old_snap_type (e->snap_type())
3162 , _old_snap_mode (e->snap_mode())
3165 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3166 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3168 _real_section = &_marker->meter();
3173 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3175 Drag::start_grab (event, cursor);
3176 show_verbose_cursor_time (adjusted_current_frame(event));
3180 MeterMarkerDrag::setup_pointer_frame_offset ()
3182 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3186 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3189 // create a dummy marker to catch events, then hide it.
3192 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3194 _marker = new MeterMarker (
3196 *_editor->meter_group,
3197 UIConfiguration::instance().color ("meter marker"),
3199 *new MeterSection (_marker->meter())
3202 /* use the new marker for the grab */
3203 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3206 TempoMap& map (_editor->session()->tempo_map());
3207 /* get current state */
3208 before_state = &map.get_state();
3211 _editor->begin_reversible_command (_("move meter mark"));
3213 _editor->begin_reversible_command (_("copy meter mark"));
3215 Timecode::BBT_Time bbt = _real_section->bbt();
3217 /* we can't add a meter where one currently exists */
3218 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3223 const double beat = map.beat_at_bbt (bbt);
3224 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3225 , beat, bbt, _real_section->position_lock_style());
3226 if (!_real_section) {
3232 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3233 if (_real_section->position_lock_style() != AudioTime) {
3234 _editor->set_snap_to (SnapToBar);
3235 _editor->set_snap_mode (SnapNormal);
3239 framepos_t pf = adjusted_current_frame (event);
3241 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3242 /* never snap to music for audio locked */
3243 pf = adjusted_current_frame (event, false);
3246 _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3248 /* fake marker meeds to stay under the mouse, unlike the real one. */
3249 _marker->set_position (adjusted_current_frame (event, false));
3251 show_verbose_cursor_time (_real_section->frame());
3255 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3257 if (!movement_occurred) {
3258 if (was_double_click()) {
3259 _editor->edit_meter_marker (*_marker);
3264 /* reinstate old snap setting */
3265 _editor->set_snap_to (_old_snap_type);
3266 _editor->set_snap_mode (_old_snap_mode);
3268 TempoMap& map (_editor->session()->tempo_map());
3270 XMLNode &after = map.get_state();
3271 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3272 _editor->commit_reversible_command ();
3274 // delete the dummy marker we used for visual representation while moving.
3275 // a new visual marker will show up automatically.
3280 MeterMarkerDrag::aborted (bool moved)
3282 _marker->set_position (_marker->meter().frame ());
3284 /* reinstate old snap setting */
3285 _editor->set_snap_to (_old_snap_type);
3286 _editor->set_snap_mode (_old_snap_mode);
3288 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3289 // delete the dummy marker we used for visual representation while moving.
3290 // a new visual marker will show up automatically.
3295 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3301 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3303 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3304 _real_section = &_marker->tempo();
3305 _movable = _real_section->movable();
3306 _grab_bpm = _real_section->note_types_per_minute();
3311 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3313 Drag::start_grab (event, cursor);
3314 if (!_real_section->active()) {
3315 show_verbose_cursor_text (_("inactive"));
3317 show_verbose_cursor_time (adjusted_current_frame (event));
3322 TempoMarkerDrag::setup_pointer_frame_offset ()
3324 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3328 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3330 if (!_real_section->active()) {
3336 // mvc drag - create a dummy marker to catch events, hide it.
3339 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3341 TempoSection section (_marker->tempo());
3343 _marker = new TempoMarker (
3345 *_editor->tempo_group,
3346 UIConfiguration::instance().color ("tempo marker"),
3348 *new TempoSection (_marker->tempo())
3351 /* use the new marker for the grab */
3352 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3355 TempoMap& map (_editor->session()->tempo_map());
3356 /* get current state */
3357 before_state = &map.get_state();
3360 _editor->begin_reversible_command (_("move tempo mark"));
3363 const Tempo tempo (_marker->tempo());
3364 const framepos_t frame = adjusted_current_frame (event) + 1;
3365 const TempoSection::Type type = _real_section->type();
3367 _editor->begin_reversible_command (_("copy tempo mark"));
3369 if (_real_section->position_lock_style() == MusicTime) {
3370 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3371 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3373 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3376 if (!_real_section) {
3384 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3385 /* use vertical movement to alter tempo .. should be log */
3386 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3388 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3390 show_verbose_cursor_text (strs.str());
3392 } else if (_movable && !_real_section->locked_to_meter()) {
3395 if (_editor->snap_musical()) {
3396 /* we can't snap to a grid that we are about to move.
3397 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3399 pf = adjusted_current_frame (event, false);
3401 pf = adjusted_current_frame (event);
3404 TempoMap& map (_editor->session()->tempo_map());
3406 /* snap to beat is 1, snap to bar is -1 (sorry) */
3407 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3409 map.gui_move_tempo (_real_section, pf, sub_num);
3411 show_verbose_cursor_time (_real_section->frame());
3413 _marker->set_position (adjusted_current_frame (event, false));
3417 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3419 if (!_real_section->active()) {
3422 if (!movement_occurred) {
3423 if (was_double_click()) {
3424 _editor->edit_tempo_marker (*_marker);
3429 TempoMap& map (_editor->session()->tempo_map());
3431 XMLNode &after = map.get_state();
3432 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3433 _editor->commit_reversible_command ();
3435 // delete the dummy marker we used for visual representation while moving.
3436 // a new visual marker will show up automatically.
3441 TempoMarkerDrag::aborted (bool moved)
3443 _marker->set_position (_marker->tempo().frame());
3445 TempoMap& map (_editor->session()->tempo_map());
3446 map.set_state (*before_state, Stateful::current_state_version);
3447 // delete the dummy (hidden) marker we used for events while moving.
3452 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3458 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3463 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3465 Drag::start_grab (event, cursor);
3466 TempoMap& map (_editor->session()->tempo_map());
3467 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3470 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3471 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3472 show_verbose_cursor_text (sstr.str());
3473 finished (event, false);
3477 BBTRulerDrag::setup_pointer_frame_offset ()
3479 TempoMap& map (_editor->session()->tempo_map());
3480 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3481 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3484 if (divisions > 0) {
3485 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3487 /* while it makes some sense for the user to determine the division to 'grab',
3488 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3489 and the result over steep tempo curves. Use sixteenths.
3491 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3494 _grab_qn = map.quarter_note_at_beat (beat);
3496 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3501 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3503 TempoMap& map (_editor->session()->tempo_map());
3506 /* get current state */
3507 before_state = &map.get_state();
3508 _editor->begin_reversible_command (_("dilate tempo"));
3513 if (_editor->snap_musical()) {
3514 pf = adjusted_current_frame (event, false);
3516 pf = adjusted_current_frame (event);
3519 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3520 /* adjust previous tempo to match pointer frame */
3521 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3524 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3525 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3526 show_verbose_cursor_text (sstr.str());
3530 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3532 if (!movement_occurred) {
3536 TempoMap& map (_editor->session()->tempo_map());
3538 XMLNode &after = map.get_state();
3539 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3540 _editor->commit_reversible_command ();
3544 BBTRulerDrag::aborted (bool moved)
3547 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3552 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3553 : Drag (e, &c.track_canvas_item(), false)
3558 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3561 /** Do all the things we do when dragging the playhead to make it look as though
3562 * we have located, without actually doing the locate (because that would cause
3563 * the diskstream buffers to be refilled, which is too slow).
3566 CursorDrag::fake_locate (framepos_t t)
3568 if (_editor->session () == 0) {
3572 _editor->playhead_cursor->set_position (t);
3574 Session* s = _editor->session ();
3575 if (s->timecode_transmission_suspended ()) {
3576 framepos_t const f = _editor->playhead_cursor->current_frame ();
3577 /* This is asynchronous so it will be sent "now"
3579 s->send_mmc_locate (f);
3580 /* These are synchronous and will be sent during the next
3583 s->queue_full_time_code ();
3584 s->queue_song_position_pointer ();
3587 show_verbose_cursor_time (t);
3588 _editor->UpdateAllTransportClocks (t);
3592 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3594 Drag::start_grab (event, c);
3595 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3597 _grab_zoom = _editor->samples_per_pixel;
3599 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3601 _editor->snap_to_with_modifier (where, event);
3603 _editor->_dragging_playhead = true;
3605 Session* s = _editor->session ();
3607 /* grab the track canvas item as well */
3609 _cursor.track_canvas_item().grab();
3612 if (_was_rolling && _stop) {
3616 if (s->is_auditioning()) {
3617 s->cancel_audition ();
3621 if (AudioEngine::instance()->connected()) {
3623 /* do this only if we're the engine is connected
3624 * because otherwise this request will never be
3625 * serviced and we'll busy wait forever. likewise,
3626 * notice if we are disconnected while waiting for the
3627 * request to be serviced.
3630 s->request_suspend_timecode_transmission ();
3631 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3632 /* twiddle our thumbs */
3637 fake_locate (where - snap_delta (event->button.state));
3641 CursorDrag::motion (GdkEvent* event, bool)
3643 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3644 _editor->snap_to_with_modifier (where, event);
3645 if (where != last_pointer_frame()) {
3646 fake_locate (where - snap_delta (event->button.state));
3651 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3653 _editor->_dragging_playhead = false;
3655 _cursor.track_canvas_item().ungrab();
3657 if (!movement_occurred && _stop) {
3661 motion (event, false);
3663 Session* s = _editor->session ();
3665 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3666 _editor->_pending_locate_request = true;
3667 s->request_resume_timecode_transmission ();
3672 CursorDrag::aborted (bool)
3674 _cursor.track_canvas_item().ungrab();
3676 if (_editor->_dragging_playhead) {
3677 _editor->session()->request_resume_timecode_transmission ();
3678 _editor->_dragging_playhead = false;
3681 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3684 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3685 : RegionDrag (e, i, p, v)
3687 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3691 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3693 Drag::start_grab (event, cursor);
3695 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3696 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3697 setup_snap_delta (r->position ());
3699 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3703 FadeInDrag::setup_pointer_frame_offset ()
3705 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3706 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3707 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3711 FadeInDrag::motion (GdkEvent* event, bool)
3713 framecnt_t fade_length;
3715 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3716 _editor->snap_to_with_modifier (pos, event);
3717 pos -= snap_delta (event->button.state);
3719 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3721 if (pos < (region->position() + 64)) {
3722 fade_length = 64; // this should be a minimum defined somewhere
3723 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3724 fade_length = region->length() - region->fade_out()->back()->when - 1;
3726 fade_length = pos - region->position();
3729 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3731 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3737 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3740 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3744 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3746 if (!movement_occurred) {
3750 framecnt_t fade_length;
3751 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3752 _editor->snap_to_with_modifier (pos, event);
3753 pos -= snap_delta (event->button.state);
3755 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3757 if (pos < (region->position() + 64)) {
3758 fade_length = 64; // this should be a minimum defined somewhere
3759 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3760 fade_length = region->length() - region->fade_out()->back()->when - 1;
3762 fade_length = pos - region->position();
3765 bool in_command = false;
3767 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3769 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3775 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3776 XMLNode &before = alist->get_state();
3778 tmp->audio_region()->set_fade_in_length (fade_length);
3779 tmp->audio_region()->set_fade_in_active (true);
3782 _editor->begin_reversible_command (_("change fade in length"));
3785 XMLNode &after = alist->get_state();
3786 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3790 _editor->commit_reversible_command ();
3795 FadeInDrag::aborted (bool)
3797 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3798 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3804 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3808 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3809 : RegionDrag (e, i, p, v)
3811 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3815 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3817 Drag::start_grab (event, cursor);
3819 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3820 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3821 setup_snap_delta (r->last_frame ());
3823 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3827 FadeOutDrag::setup_pointer_frame_offset ()
3829 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3830 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3831 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3835 FadeOutDrag::motion (GdkEvent* event, bool)
3837 framecnt_t fade_length;
3839 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3840 _editor->snap_to_with_modifier (pos, event);
3841 pos -= snap_delta (event->button.state);
3843 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3845 if (pos > (region->last_frame() - 64)) {
3846 fade_length = 64; // this should really be a minimum fade defined somewhere
3847 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3848 fade_length = region->length() - region->fade_in()->back()->when - 1;
3850 fade_length = region->last_frame() - pos;
3853 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3855 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3861 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3864 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3868 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3870 if (!movement_occurred) {
3874 framecnt_t fade_length;
3876 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3877 _editor->snap_to_with_modifier (pos, event);
3878 pos -= snap_delta (event->button.state);
3880 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3882 if (pos > (region->last_frame() - 64)) {
3883 fade_length = 64; // this should really be a minimum fade defined somewhere
3884 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3885 fade_length = region->length() - region->fade_in()->back()->when - 1;
3887 fade_length = region->last_frame() - pos;
3890 bool in_command = false;
3892 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3894 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3900 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3901 XMLNode &before = alist->get_state();
3903 tmp->audio_region()->set_fade_out_length (fade_length);
3904 tmp->audio_region()->set_fade_out_active (true);
3907 _editor->begin_reversible_command (_("change fade out length"));
3910 XMLNode &after = alist->get_state();
3911 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3915 _editor->commit_reversible_command ();
3920 FadeOutDrag::aborted (bool)
3922 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3923 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3929 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3933 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3935 , _selection_changed (false)
3937 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3938 Gtk::Window* toplevel = _editor->current_toplevel();
3939 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3943 _points.push_back (ArdourCanvas::Duple (0, 0));
3945 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3948 MarkerDrag::~MarkerDrag ()
3950 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3955 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3957 location = new Location (*l);
3958 markers.push_back (m);
3963 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3965 Drag::start_grab (event, cursor);
3969 Location *location = _editor->find_location_from_marker (_marker, is_start);
3970 _editor->_dragging_edit_point = true;
3972 update_item (location);
3974 // _drag_line->show();
3975 // _line->raise_to_top();
3978 show_verbose_cursor_time (location->start());
3980 show_verbose_cursor_time (location->end());
3982 setup_snap_delta (is_start ? location->start() : location->end());
3984 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3987 case Selection::Toggle:
3988 /* we toggle on the button release */
3990 case Selection::Set:
3991 if (!_editor->selection->selected (_marker)) {
3992 _editor->selection->set (_marker);
3993 _selection_changed = true;
3996 case Selection::Extend:
3998 Locations::LocationList ll;
3999 list<ArdourMarker*> to_add;
4001 _editor->selection->markers.range (s, e);
4002 s = min (_marker->position(), s);
4003 e = max (_marker->position(), e);
4006 if (e < max_framepos) {
4009 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4010 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4011 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4014 to_add.push_back (lm->start);
4017 to_add.push_back (lm->end);
4021 if (!to_add.empty()) {
4022 _editor->selection->add (to_add);
4023 _selection_changed = true;
4027 case Selection::Add:
4028 _editor->selection->add (_marker);
4029 _selection_changed = true;
4034 /* Set up copies for us to manipulate during the drag
4037 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4039 Location* l = _editor->find_location_from_marker (*i, is_start);
4046 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4048 /* range: check that the other end of the range isn't
4051 CopiedLocationInfo::iterator x;
4052 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4053 if (*(*x).location == *l) {
4057 if (x == _copied_locations.end()) {
4058 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4060 (*x).markers.push_back (*i);
4061 (*x).move_both = true;
4069 MarkerDrag::setup_pointer_frame_offset ()
4072 Location *location = _editor->find_location_from_marker (_marker, is_start);
4073 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4077 MarkerDrag::motion (GdkEvent* event, bool)
4079 framecnt_t f_delta = 0;
4081 bool move_both = false;
4082 Location *real_location;
4083 Location *copy_location = 0;
4084 framecnt_t const sd = snap_delta (event->button.state);
4086 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4087 framepos_t next = newframe;
4089 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4093 CopiedLocationInfo::iterator x;
4095 /* find the marker we're dragging, and compute the delta */
4097 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4099 copy_location = (*x).location;
4101 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4103 /* this marker is represented by this
4104 * CopiedLocationMarkerInfo
4107 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4112 if (real_location->is_mark()) {
4113 f_delta = newframe - copy_location->start();
4117 switch (_marker->type()) {
4118 case ArdourMarker::SessionStart:
4119 case ArdourMarker::RangeStart:
4120 case ArdourMarker::LoopStart:
4121 case ArdourMarker::PunchIn:
4122 f_delta = newframe - copy_location->start();
4125 case ArdourMarker::SessionEnd:
4126 case ArdourMarker::RangeEnd:
4127 case ArdourMarker::LoopEnd:
4128 case ArdourMarker::PunchOut:
4129 f_delta = newframe - copy_location->end();
4132 /* what kind of marker is this ? */
4141 if (x == _copied_locations.end()) {
4142 /* hmm, impossible - we didn't find the dragged marker */
4146 /* now move them all */
4148 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4150 copy_location = x->location;
4152 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4156 if (real_location->locked()) {
4160 if (copy_location->is_mark()) {
4164 copy_location->set_start (copy_location->start() + f_delta);
4168 framepos_t new_start = copy_location->start() + f_delta;
4169 framepos_t new_end = copy_location->end() + f_delta;
4171 if (is_start) { // start-of-range marker
4173 if (move_both || (*x).move_both) {
4174 copy_location->set_start (new_start);
4175 copy_location->set_end (new_end);
4176 } else if (new_start < copy_location->end()) {
4177 copy_location->set_start (new_start);
4178 } else if (newframe > 0) {
4179 //_editor->snap_to (next, RoundUpAlways, true);
4180 copy_location->set_end (next);
4181 copy_location->set_start (newframe);
4184 } else { // end marker
4186 if (move_both || (*x).move_both) {
4187 copy_location->set_end (new_end);
4188 copy_location->set_start (new_start);
4189 } else if (new_end > copy_location->start()) {
4190 copy_location->set_end (new_end);
4191 } else if (newframe > 0) {
4192 //_editor->snap_to (next, RoundDownAlways, true);
4193 copy_location->set_start (next);
4194 copy_location->set_end (newframe);
4199 update_item (copy_location);
4201 /* now lookup the actual GUI items used to display this
4202 * location and move them to wherever the copy of the location
4203 * is now. This means that the logic in ARDOUR::Location is
4204 * still enforced, even though we are not (yet) modifying
4205 * the real Location itself.
4208 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4211 lm->set_position (copy_location->start(), copy_location->end());
4216 assert (!_copied_locations.empty());
4218 show_verbose_cursor_time (newframe);
4222 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4224 if (!movement_occurred) {
4226 if (was_double_click()) {
4227 _editor->rename_marker (_marker);
4231 /* just a click, do nothing but finish
4232 off the selection process
4235 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4237 case Selection::Set:
4238 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4239 _editor->selection->set (_marker);
4240 _selection_changed = true;
4244 case Selection::Toggle:
4245 /* we toggle on the button release, click only */
4246 _editor->selection->toggle (_marker);
4247 _selection_changed = true;
4251 case Selection::Extend:
4252 case Selection::Add:
4256 if (_selection_changed) {
4257 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4258 _editor->commit_reversible_selection_op();
4264 _editor->_dragging_edit_point = false;
4266 XMLNode &before = _editor->session()->locations()->get_state();
4267 bool in_command = false;
4269 MarkerSelection::iterator i;
4270 CopiedLocationInfo::iterator x;
4273 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4274 x != _copied_locations.end() && i != _editor->selection->markers.end();
4277 Location * location = _editor->find_location_from_marker (*i, is_start);
4281 if (location->locked()) {
4285 _editor->begin_reversible_command ( _("move marker") );
4288 if (location->is_mark()) {
4289 location->set_start (((*x).location)->start());
4291 location->set (((*x).location)->start(), ((*x).location)->end());
4294 if (location->is_session_range()) {
4295 _editor->session()->set_end_is_free (false);
4301 XMLNode &after = _editor->session()->locations()->get_state();
4302 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4303 _editor->commit_reversible_command ();
4308 MarkerDrag::aborted (bool movement_occurred)
4310 if (!movement_occurred) {
4314 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4316 /* move all markers to their original location */
4319 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4322 Location * location = _editor->find_location_from_marker (*m, is_start);
4325 (*m)->set_position (is_start ? location->start() : location->end());
4332 MarkerDrag::update_item (Location*)
4337 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4339 , _fixed_grab_x (0.0)
4340 , _fixed_grab_y (0.0)
4341 , _cumulative_x_drag (0.0)
4342 , _cumulative_y_drag (0.0)
4346 if (_zero_gain_fraction < 0.0) {
4347 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4350 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4352 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4358 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4360 Drag::start_grab (event, _editor->cursors()->fader);
4362 // start the grab at the center of the control point so
4363 // the point doesn't 'jump' to the mouse after the first drag
4364 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4365 _fixed_grab_y = _point->get_y();
4367 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4368 setup_snap_delta (pos);
4370 float const fraction = 1 - (_point->get_y() / _point->line().height());
4371 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4373 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4375 if (!_point->can_slide ()) {
4376 _x_constrained = true;
4381 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4383 double dx = _drags->current_pointer_x() - last_pointer_x();
4384 double dy = current_pointer_y() - last_pointer_y();
4385 bool need_snap = true;
4387 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4393 /* coordinate in pixels relative to the start of the region (for region-based automation)
4394 or track (for track-based automation) */
4395 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4396 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4398 // calculate zero crossing point. back off by .01 to stay on the
4399 // positive side of zero
4400 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4402 if (_x_constrained) {
4405 if (_y_constrained) {
4409 _cumulative_x_drag = cx - _fixed_grab_x;
4410 _cumulative_y_drag = cy - _fixed_grab_y;
4414 cy = min ((double) _point->line().height(), cy);
4416 // make sure we hit zero when passing through
4417 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4421 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4422 if (!_x_constrained && need_snap) {
4423 _editor->snap_to_with_modifier (cx_frames, event);
4426 cx_frames -= snap_delta (event->button.state);
4427 cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4429 float const fraction = 1.0 - (cy / _point->line().height());
4432 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4433 _editor->begin_reversible_command (_("automation event move"));
4434 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4436 pair<double, float> result;
4437 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4439 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4443 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4445 if (!movement_occurred) {
4448 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4449 _editor->reset_point_selection ();
4453 _point->line().end_drag (_pushing, _final_index);
4454 _editor->commit_reversible_command ();
4459 ControlPointDrag::aborted (bool)
4461 _point->line().reset ();
4465 ControlPointDrag::active (Editing::MouseMode m)
4467 if (m == Editing::MouseDraw) {
4468 /* always active in mouse draw */
4472 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4473 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4476 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4479 , _fixed_grab_x (0.0)
4480 , _fixed_grab_y (0.0)
4481 , _cumulative_y_drag (0)
4485 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4489 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4491 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4494 _item = &_line->grab_item ();
4496 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4497 origin, and ditto for y.
4500 double mx = event->button.x;
4501 double my = event->button.y;
4503 _line->grab_item().canvas_to_item (mx, my);
4505 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4507 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4508 /* no adjacent points */
4512 Drag::start_grab (event, _editor->cursors()->fader);
4514 /* store grab start in item frame */
4515 double const bx = _line->nth (_before)->get_x();
4516 double const ax = _line->nth (_after)->get_x();
4517 double const click_ratio = (ax - mx) / (ax - bx);
4519 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4524 double fraction = 1.0 - (cy / _line->height());
4526 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4530 LineDrag::motion (GdkEvent* event, bool first_move)
4532 double dy = current_pointer_y() - last_pointer_y();
4534 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4538 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4540 _cumulative_y_drag = cy - _fixed_grab_y;
4543 cy = min ((double) _line->height(), cy);
4545 double const fraction = 1.0 - (cy / _line->height());
4549 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4551 _editor->begin_reversible_command (_("automation range move"));
4552 _line->start_drag_line (_before, _after, initial_fraction);
4555 /* we are ignoring x position for this drag, so we can just pass in anything */
4556 pair<double, float> result;
4558 result = _line->drag_motion (0, fraction, true, false, ignored);
4559 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4563 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4565 if (movement_occurred) {
4566 motion (event, false);
4567 _line->end_drag (false, 0);
4568 _editor->commit_reversible_command ();
4570 /* add a new control point on the line */
4572 AutomationTimeAxisView* atv;
4574 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4575 framepos_t where = grab_frame ();
4578 double cy = _fixed_grab_y;
4580 _line->grab_item().item_to_canvas (cx, cy);
4582 atv->add_automation_event (event, where, cy, false);
4583 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4584 AudioRegionView* arv;
4586 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4587 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4594 LineDrag::aborted (bool)
4599 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4603 _region_view_grab_x (0.0),
4604 _cumulative_x_drag (0),
4608 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4612 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4614 Drag::start_grab (event);
4616 _line = reinterpret_cast<Line*> (_item);
4619 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4621 double cx = event->button.x;
4622 double cy = event->button.y;
4624 _item->parent()->canvas_to_item (cx, cy);
4626 /* store grab start in parent frame */
4627 _region_view_grab_x = cx;
4629 _before = *(float*) _item->get_data ("position");
4631 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4633 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4637 FeatureLineDrag::motion (GdkEvent*, bool)
4639 double dx = _drags->current_pointer_x() - last_pointer_x();
4641 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4643 _cumulative_x_drag += dx;
4645 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4654 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4656 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4658 float *pos = new float;
4661 _line->set_data ("position", pos);
4667 FeatureLineDrag::finished (GdkEvent*, bool)
4669 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4670 _arv->update_transient(_before, _before);
4674 FeatureLineDrag::aborted (bool)
4679 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4681 , _vertical_only (false)
4683 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4687 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4689 Drag::start_grab (event);
4690 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4694 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4701 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4703 framepos_t grab = grab_frame ();
4704 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4705 _editor->snap_to_with_modifier (grab, event);
4707 grab = raw_grab_frame ();
4710 /* base start and end on initial click position */
4720 if (current_pointer_y() < grab_y()) {
4721 y1 = current_pointer_y();
4724 y2 = current_pointer_y();
4728 if (start != end || y1 != y2) {
4730 double x1 = _editor->sample_to_pixel (start);
4731 double x2 = _editor->sample_to_pixel (end);
4732 const double min_dimension = 2.0;
4734 if (_vertical_only) {
4735 /* fixed 10 pixel width */
4739 x2 = min (x1 - min_dimension, x2);
4741 x2 = max (x1 + min_dimension, x2);
4746 y2 = min (y1 - min_dimension, y2);
4748 y2 = max (y1 + min_dimension, y2);
4751 /* translate rect into item space and set */
4753 ArdourCanvas::Rect r (x1, y1, x2, y2);
4755 /* this drag is a _trackview_only == true drag, so the y1 and
4756 * y2 (computed using current_pointer_y() and grab_y()) will be
4757 * relative to the top of the trackview group). The
4758 * rubberband rect has the same parent/scroll offset as the
4759 * the trackview group, so we can use the "r" rect directly
4760 * to set the shape of the rubberband.
4763 _editor->rubberband_rect->set (r);
4764 _editor->rubberband_rect->show();
4765 _editor->rubberband_rect->raise_to_top();
4767 show_verbose_cursor_time (pf);
4769 do_select_things (event, true);
4774 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4778 framepos_t grab = grab_frame ();
4779 framepos_t lpf = last_pointer_frame ();
4781 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4782 grab = raw_grab_frame ();
4783 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4797 if (current_pointer_y() < grab_y()) {
4798 y1 = current_pointer_y();
4801 y2 = current_pointer_y();
4805 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4809 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4811 if (movement_occurred) {
4813 motion (event, false);
4814 do_select_things (event, false);
4820 bool do_deselect = true;
4821 MidiTimeAxisView* mtv;
4823 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4825 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4826 /* nothing selected */
4827 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4828 do_deselect = false;
4832 /* do not deselect if Primary or Tertiary (toggle-select or
4833 * extend-select are pressed.
4836 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4837 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4844 _editor->rubberband_rect->hide();
4848 RubberbandSelectDrag::aborted (bool)
4850 _editor->rubberband_rect->hide ();
4853 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4854 : RegionDrag (e, i, p, v)
4856 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4860 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4862 Drag::start_grab (event, cursor);
4864 _editor->get_selection().add (_primary);
4866 framepos_t where = _primary->region()->position();
4867 setup_snap_delta (where);
4869 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4873 TimeFXDrag::motion (GdkEvent* event, bool)
4875 RegionView* rv = _primary;
4876 StreamView* cv = rv->get_time_axis_view().view ();
4878 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4879 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4880 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4881 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4882 _editor->snap_to_with_modifier (pf, event);
4883 pf -= snap_delta (event->button.state);
4885 if (pf > rv->region()->position()) {
4886 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4889 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4893 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4895 /* this may have been a single click, no drag. We still want the dialog
4896 to show up in that case, so that the user can manually edit the
4897 parameters for the timestretch.
4900 float fraction = 1.0;
4902 if (movement_occurred) {
4904 motion (event, false);
4906 _primary->get_time_axis_view().hide_timestretch ();
4908 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4910 if (adjusted_frame_pos < _primary->region()->position()) {
4911 /* backwards drag of the left edge - not usable */
4915 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4917 fraction = (double) newlen / (double) _primary->region()->length();
4919 #ifndef USE_RUBBERBAND
4920 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4921 if (_primary->region()->data_type() == DataType::AUDIO) {
4922 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4927 if (!_editor->get_selection().regions.empty()) {
4928 /* primary will already be included in the selection, and edit
4929 group shared editing will propagate selection across
4930 equivalent regions, so just use the current region
4934 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4935 error << _("An error occurred while executing time stretch operation") << endmsg;
4941 TimeFXDrag::aborted (bool)
4943 _primary->get_time_axis_view().hide_timestretch ();
4946 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4949 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4953 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4955 Drag::start_grab (event);
4959 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4961 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4965 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4967 if (movement_occurred && _editor->session()) {
4968 /* make sure we stop */
4969 _editor->session()->request_transport_speed (0.0);
4974 ScrubDrag::aborted (bool)
4979 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4983 , _time_selection_at_start (!_editor->get_selection().time.empty())
4985 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4987 if (_time_selection_at_start) {
4988 start_at_start = _editor->get_selection().time.start();
4989 end_at_start = _editor->get_selection().time.end_frame();
4994 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4996 if (_editor->session() == 0) {
5000 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5002 switch (_operation) {
5003 case CreateSelection:
5004 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5009 cursor = _editor->cursors()->selector;
5010 Drag::start_grab (event, cursor);
5013 case SelectionStartTrim:
5014 if (_editor->clicked_axisview) {
5015 _editor->clicked_axisview->order_selection_trims (_item, true);
5017 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5020 case SelectionEndTrim:
5021 if (_editor->clicked_axisview) {
5022 _editor->clicked_axisview->order_selection_trims (_item, false);
5024 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5028 Drag::start_grab (event, cursor);
5031 case SelectionExtend:
5032 Drag::start_grab (event, cursor);
5036 if (_operation == SelectionMove) {
5037 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5039 show_verbose_cursor_time (adjusted_current_frame (event));
5044 SelectionDrag::setup_pointer_frame_offset ()
5046 switch (_operation) {
5047 case CreateSelection:
5048 _pointer_frame_offset = 0;
5051 case SelectionStartTrim:
5053 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5056 case SelectionEndTrim:
5057 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5060 case SelectionExtend:
5066 SelectionDrag::motion (GdkEvent* event, bool first_move)
5068 framepos_t start = 0;
5070 framecnt_t length = 0;
5071 framecnt_t distance = 0;
5073 framepos_t const pending_position = adjusted_current_frame (event);
5075 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5079 switch (_operation) {
5080 case CreateSelection:
5082 framepos_t grab = grab_frame ();
5085 grab = adjusted_current_frame (event, false);
5086 if (grab < pending_position) {
5087 _editor->snap_to (grab, RoundDownMaybe);
5089 _editor->snap_to (grab, RoundUpMaybe);
5093 if (pending_position < grab) {
5094 start = pending_position;
5097 end = pending_position;
5101 /* first drag: Either add to the selection
5102 or create a new selection
5109 /* adding to the selection */
5110 _editor->set_selected_track_as_side_effect (Selection::Add);
5111 _editor->clicked_selection = _editor->selection->add (start, end);
5118 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5119 _editor->set_selected_track_as_side_effect (Selection::Set);
5122 _editor->clicked_selection = _editor->selection->set (start, end);
5126 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5127 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5128 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5130 _editor->selection->add (atest);
5134 /* select all tracks within the rectangle that we've marked out so far */
5135 TrackViewList new_selection;
5136 TrackViewList& all_tracks (_editor->track_views);
5138 ArdourCanvas::Coord const top = grab_y();
5139 ArdourCanvas::Coord const bottom = current_pointer_y();
5141 if (top >= 0 && bottom >= 0) {
5143 //first, find the tracks that are covered in the y range selection
5144 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5145 if ((*i)->covered_by_y_range (top, bottom)) {
5146 new_selection.push_back (*i);
5150 //now find any tracks that are GROUPED with the tracks we selected
5151 TrackViewList grouped_add = new_selection;
5152 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5153 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5154 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5155 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5156 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5157 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5158 grouped_add.push_back (*j);
5163 //now compare our list with the current selection, and add or remove as necessary
5164 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5165 TrackViewList tracks_to_add;
5166 TrackViewList tracks_to_remove;
5167 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5168 if ( !_editor->selection->tracks.contains ( *i ) )
5169 tracks_to_add.push_back ( *i );
5170 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5171 if ( !grouped_add.contains ( *i ) )
5172 tracks_to_remove.push_back ( *i );
5173 _editor->selection->add(tracks_to_add);
5174 _editor->selection->remove(tracks_to_remove);
5180 case SelectionStartTrim:
5182 end = _editor->selection->time[_editor->clicked_selection].end;
5184 if (pending_position > end) {
5187 start = pending_position;
5191 case SelectionEndTrim:
5193 start = _editor->selection->time[_editor->clicked_selection].start;
5195 if (pending_position < start) {
5198 end = pending_position;
5205 start = _editor->selection->time[_editor->clicked_selection].start;
5206 end = _editor->selection->time[_editor->clicked_selection].end;
5208 length = end - start;
5209 distance = pending_position - start;
5210 start = pending_position;
5211 _editor->snap_to (start);
5213 end = start + length;
5217 case SelectionExtend:
5222 switch (_operation) {
5224 if (_time_selection_at_start) {
5225 _editor->selection->move_time (distance);
5229 _editor->selection->replace (_editor->clicked_selection, start, end);
5233 if (_operation == SelectionMove) {
5234 show_verbose_cursor_time(start);
5236 show_verbose_cursor_time(pending_position);
5241 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5243 Session* s = _editor->session();
5245 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5246 if (movement_occurred) {
5247 motion (event, false);
5248 /* XXX this is not object-oriented programming at all. ick */
5249 if (_editor->selection->time.consolidate()) {
5250 _editor->selection->TimeChanged ();
5253 /* XXX what if its a music time selection? */
5255 if (s->get_play_range() && s->transport_rolling()) {
5256 s->request_play_range (&_editor->selection->time, true);
5257 } else if (!s->config.get_external_sync()) {
5258 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5259 if (_operation == SelectionEndTrim)
5260 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5262 s->request_locate (_editor->get_selection().time.start());
5266 if (_editor->get_selection().time.length() != 0) {
5267 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5269 s->clear_range_selection ();
5274 /* just a click, no pointer movement.
5277 if (_operation == SelectionExtend) {
5278 if (_time_selection_at_start) {
5279 framepos_t pos = adjusted_current_frame (event, false);
5280 framepos_t start = min (pos, start_at_start);
5281 framepos_t end = max (pos, end_at_start);
5282 _editor->selection->set (start, end);
5285 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5286 if (_editor->clicked_selection) {
5287 _editor->selection->remove (_editor->clicked_selection);
5290 if (!_editor->clicked_selection) {
5291 _editor->selection->clear_time();
5296 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5297 _editor->selection->set (_editor->clicked_axisview);
5300 if (s && s->get_play_range () && s->transport_rolling()) {
5301 s->request_stop (false, false);
5306 _editor->stop_canvas_autoscroll ();
5307 _editor->clicked_selection = 0;
5308 _editor->commit_reversible_selection_op ();
5312 SelectionDrag::aborted (bool)
5317 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5318 : Drag (e, i, false),
5322 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5324 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5325 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5326 physical_screen_height (_editor->current_toplevel()->get_window())));
5327 _drag_rect->hide ();
5329 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5330 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5333 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5335 /* normal canvas items will be cleaned up when their parent group is deleted. But
5336 this item is created as the child of a long-lived parent group, and so we
5337 need to explicitly delete it.
5343 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5345 if (_editor->session() == 0) {
5349 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5351 if (!_editor->temp_location) {
5352 _editor->temp_location = new Location (*_editor->session());
5355 switch (_operation) {
5356 case CreateSkipMarker:
5357 case CreateRangeMarker:
5358 case CreateTransportMarker:
5359 case CreateCDMarker:
5361 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5366 cursor = _editor->cursors()->selector;
5370 Drag::start_grab (event, cursor);
5372 show_verbose_cursor_time (adjusted_current_frame (event));
5376 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5378 framepos_t start = 0;
5380 ArdourCanvas::Rectangle *crect;
5382 switch (_operation) {
5383 case CreateSkipMarker:
5384 crect = _editor->range_bar_drag_rect;
5386 case CreateRangeMarker:
5387 crect = _editor->range_bar_drag_rect;
5389 case CreateTransportMarker:
5390 crect = _editor->transport_bar_drag_rect;
5392 case CreateCDMarker:
5393 crect = _editor->cd_marker_bar_drag_rect;
5396 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5401 framepos_t const pf = adjusted_current_frame (event);
5403 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5404 framepos_t grab = grab_frame ();
5405 _editor->snap_to (grab);
5407 if (pf < grab_frame()) {
5415 /* first drag: Either add to the selection
5416 or create a new selection.
5421 _editor->temp_location->set (start, end);
5425 update_item (_editor->temp_location);
5427 //_drag_rect->raise_to_top();
5433 _editor->temp_location->set (start, end);
5435 double x1 = _editor->sample_to_pixel (start);
5436 double x2 = _editor->sample_to_pixel (end);
5440 update_item (_editor->temp_location);
5443 show_verbose_cursor_time (pf);
5448 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5450 Location * newloc = 0;
5454 if (movement_occurred) {
5455 motion (event, false);
5458 switch (_operation) {
5459 case CreateSkipMarker:
5460 case CreateRangeMarker:
5461 case CreateCDMarker:
5463 XMLNode &before = _editor->session()->locations()->get_state();
5464 if (_operation == CreateSkipMarker) {
5465 _editor->begin_reversible_command (_("new skip marker"));
5466 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5467 flags = Location::IsRangeMarker | Location::IsSkip;
5468 _editor->range_bar_drag_rect->hide();
5469 } else if (_operation == CreateCDMarker) {
5470 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5471 _editor->begin_reversible_command (_("new CD marker"));
5472 flags = Location::IsRangeMarker | Location::IsCDMarker;
5473 _editor->cd_marker_bar_drag_rect->hide();
5475 _editor->begin_reversible_command (_("new skip marker"));
5476 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5477 flags = Location::IsRangeMarker;
5478 _editor->range_bar_drag_rect->hide();
5480 newloc = new Location (
5481 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5484 _editor->session()->locations()->add (newloc, true);
5485 XMLNode &after = _editor->session()->locations()->get_state();
5486 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5487 _editor->commit_reversible_command ();
5491 case CreateTransportMarker:
5492 // popup menu to pick loop or punch
5493 _editor->new_transport_marker_context_menu (&event->button, _item);
5499 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5501 if (_operation == CreateTransportMarker) {
5503 /* didn't drag, so just locate */
5505 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5507 } else if (_operation == CreateCDMarker) {
5509 /* didn't drag, but mark is already created so do
5512 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5517 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5519 if (end == max_framepos) {
5520 end = _editor->session()->current_end_frame ();
5523 if (start == max_framepos) {
5524 start = _editor->session()->current_start_frame ();
5527 switch (_editor->mouse_mode) {
5529 /* find the two markers on either side and then make the selection from it */
5530 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5534 /* find the two markers on either side of the click and make the range out of it */
5535 _editor->selection->set (start, end);
5544 _editor->stop_canvas_autoscroll ();
5548 RangeMarkerBarDrag::aborted (bool movement_occurred)
5550 if (movement_occurred) {
5551 _drag_rect->hide ();
5556 RangeMarkerBarDrag::update_item (Location* location)
5558 double const x1 = _editor->sample_to_pixel (location->start());
5559 double const x2 = _editor->sample_to_pixel (location->end());
5561 _drag_rect->set_x0 (x1);
5562 _drag_rect->set_x1 (x2);
5565 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5567 , _cumulative_dx (0)
5568 , _cumulative_dy (0)
5569 , _was_selected (false)
5571 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5573 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5575 _region = &_primary->region_view ();
5576 _note_height = _region->midi_stream_view()->note_height ();
5580 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5582 Drag::start_grab (event);
5583 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5585 if (!(_was_selected = _primary->selected())) {
5587 /* tertiary-click means extend selection - we'll do that on button release,
5588 so don't add it here, because otherwise we make it hard to figure
5589 out the "extend-to" range.
5592 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5595 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5598 _region->note_selected (_primary, true);
5600 _editor->get_selection().clear_points();
5601 _region->unique_select (_primary);
5607 /** @return Current total drag x change in frames */
5609 NoteDrag::total_dx (const guint state) const
5611 if (_x_constrained) {
5614 TempoMap& map (_editor->session()->tempo_map());
5617 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5619 /* primary note time */
5620 double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5621 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5623 /* new time of the primary note in session frames */
5624 frameoffset_t st = n + dx + snap_delta (state);
5626 framepos_t const rp = _region->region()->position ();
5628 /* prevent the note being dragged earlier than the region's position */
5631 /* possibly snap and return corresponding delta */
5635 if (ArdourKeyboard::indicates_snap (state)) {
5636 if (_editor->snap_mode () != SnapOff) {
5640 if (_editor->snap_mode () == SnapOff) {
5642 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5643 if (ArdourKeyboard::indicates_snap_delta (state)) {
5651 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5652 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5654 ret = st - n - snap_delta (state);
5659 /** @return Current total drag y change in note number */
5661 NoteDrag::total_dy () const
5663 if (_y_constrained) {
5667 MidiStreamView* msv = _region->midi_stream_view ();
5668 double const y = _region->midi_view()->y_position ();
5669 /* new current note */
5670 uint8_t n = msv->y_to_note (current_pointer_y () - y);
5672 n = max (msv->lowest_note(), n);
5673 n = min (msv->highest_note(), n);
5674 /* and work out delta */
5675 return n - msv->y_to_note (grab_y() - y);
5679 NoteDrag::motion (GdkEvent * event, bool)
5681 /* Total change in x and y since the start of the drag */
5682 frameoffset_t const dx = total_dx (event->button.state);
5683 int8_t const dy = total_dy ();
5685 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5686 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5687 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5690 _cumulative_dx += tdx;
5691 _cumulative_dy += tdy;
5693 int8_t note_delta = total_dy();
5696 _region->move_selection (tdx, tdy, note_delta);
5698 /* the new note value may be the same as the old one, but we
5699 * don't know what that means because the selection may have
5700 * involved more than one note and we might be doing something
5701 * odd with them. so show the note value anyway, always.
5704 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5706 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5712 NoteDrag::finished (GdkEvent* ev, bool moved)
5715 /* no motion - select note */
5717 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5718 _editor->current_mouse_mode() == Editing::MouseDraw) {
5720 bool changed = false;
5722 if (_was_selected) {
5723 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5725 _region->note_deselected (_primary);
5728 _editor->get_selection().clear_points();
5729 _region->unique_select (_primary);
5733 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5734 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5736 if (!extend && !add && _region->selection_size() > 1) {
5737 _editor->get_selection().clear_points();
5738 _region->unique_select (_primary);
5740 } else if (extend) {
5741 _region->note_selected (_primary, true, true);
5744 /* it was added during button press */
5751 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5752 _editor->commit_reversible_selection_op();
5756 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5761 NoteDrag::aborted (bool)
5766 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5767 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5768 : Drag (editor, atv->base_item ())
5770 , _y_origin (atv->y_position())
5771 , _nothing_to_drag (false)
5773 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5774 setup (atv->lines ());
5777 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5778 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5779 : Drag (editor, rv->get_canvas_group ())
5781 , _y_origin (rv->get_time_axis_view().y_position())
5782 , _nothing_to_drag (false)
5785 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5787 list<boost::shared_ptr<AutomationLine> > lines;
5789 AudioRegionView* audio_view;
5790 AutomationRegionView* automation_view;
5791 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5792 lines.push_back (audio_view->get_gain_line ());
5793 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5794 lines.push_back (automation_view->line ());
5797 error << _("Automation range drag created for invalid region type") << endmsg;
5803 /** @param lines AutomationLines to drag.
5804 * @param offset Offset from the session start to the points in the AutomationLines.
5807 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5809 /* find the lines that overlap the ranges being dragged */
5810 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5811 while (i != lines.end ()) {
5812 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5815 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5817 /* check this range against all the AudioRanges that we are using */
5818 list<AudioRange>::const_iterator k = _ranges.begin ();
5819 while (k != _ranges.end()) {
5820 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5826 /* add it to our list if it overlaps at all */
5827 if (k != _ranges.end()) {
5832 _lines.push_back (n);
5838 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5842 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5844 return 1.0 - ((global_y - _y_origin) / line->height());
5848 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5850 const double v = list->eval(x);
5851 return _integral ? rint(v) : v;
5855 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5857 Drag::start_grab (event, cursor);
5859 /* Get line states before we start changing things */
5860 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5861 i->state = &i->line->get_state ();
5862 i->original_fraction = y_fraction (i->line, current_pointer_y());
5865 if (_ranges.empty()) {
5867 /* No selected time ranges: drag all points */
5868 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5869 uint32_t const N = i->line->npoints ();
5870 for (uint32_t j = 0; j < N; ++j) {
5871 i->points.push_back (i->line->nth (j));
5877 if (_nothing_to_drag) {
5883 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5885 if (_nothing_to_drag && !first_move) {
5890 _editor->begin_reversible_command (_("automation range move"));
5892 if (!_ranges.empty()) {
5894 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5896 framecnt_t const half = (i->start + i->end) / 2;
5898 /* find the line that this audio range starts in */
5899 list<Line>::iterator j = _lines.begin();
5900 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5904 if (j != _lines.end()) {
5905 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5907 /* j is the line that this audio range starts in; fade into it;
5908 64 samples length plucked out of thin air.
5911 framepos_t a = i->start + 64;
5916 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5917 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5919 XMLNode &before = the_list->get_state();
5920 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5921 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5923 if (add_p || add_q) {
5924 _editor->session()->add_command (
5925 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5929 /* same thing for the end */
5932 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5936 if (j != _lines.end()) {
5937 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5939 /* j is the line that this audio range starts in; fade out of it;
5940 64 samples length plucked out of thin air.
5943 framepos_t b = i->end - 64;
5948 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5949 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5951 XMLNode &before = the_list->get_state();
5952 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5953 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5955 if (add_p || add_q) {
5956 _editor->session()->add_command (
5957 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5962 _nothing_to_drag = true;
5964 /* Find all the points that should be dragged and put them in the relevant
5965 points lists in the Line structs.
5968 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5970 uint32_t const N = i->line->npoints ();
5971 for (uint32_t j = 0; j < N; ++j) {
5973 /* here's a control point on this line */
5974 ControlPoint* p = i->line->nth (j);
5975 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5977 /* see if it's inside a range */
5978 list<AudioRange>::const_iterator k = _ranges.begin ();
5979 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5983 if (k != _ranges.end()) {
5984 /* dragging this point */
5985 _nothing_to_drag = false;
5986 i->points.push_back (p);
5992 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5993 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5997 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5998 float const f = y_fraction (l->line, current_pointer_y());
5999 /* we are ignoring x position for this drag, so we can just pass in anything */
6000 pair<double, float> result;
6002 result = l->line->drag_motion (0, f, true, false, ignored);
6003 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6008 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6010 if (_nothing_to_drag || !motion_occurred) {
6014 motion (event, false);
6015 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6016 i->line->end_drag (false, 0);
6019 _editor->commit_reversible_command ();
6023 AutomationRangeDrag::aborted (bool)
6025 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6030 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6032 , initial_time_axis_view (itav)
6034 /* note that time_axis_view may be null if the regionview was created
6035 * as part of a copy operation.
6037 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6038 layer = v->region()->layer ();
6039 initial_y = v->get_canvas_group()->position().y;
6040 initial_playlist = v->region()->playlist ();
6041 initial_position = v->region()->position ();
6042 initial_end = v->region()->position () + v->region()->length ();
6045 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6046 : Drag (e, i->canvas_item ())
6049 , _cumulative_dx (0)
6051 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6052 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6057 PatchChangeDrag::motion (GdkEvent* ev, bool)
6059 framepos_t f = adjusted_current_frame (ev);
6060 boost::shared_ptr<Region> r = _region_view->region ();
6061 f = max (f, r->position ());
6062 f = min (f, r->last_frame ());
6064 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6065 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6066 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6067 _cumulative_dx = dxu;
6071 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6073 if (!movement_occurred) {
6074 if (was_double_click()) {
6075 _region_view->edit_patch_change (_patch_change);
6080 boost::shared_ptr<Region> r (_region_view->region ());
6081 framepos_t f = adjusted_current_frame (ev);
6082 f = max (f, r->position ());
6083 f = min (f, r->last_frame ());
6085 _region_view->move_patch_change (
6087 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6092 PatchChangeDrag::aborted (bool)
6094 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6098 PatchChangeDrag::setup_pointer_frame_offset ()
6100 boost::shared_ptr<Region> region = _region_view->region ();
6101 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6104 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6105 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6112 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6114 _region_view->update_drag_selection (
6116 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6120 MidiRubberbandSelectDrag::deselect_things ()
6125 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6126 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6129 _vertical_only = true;
6133 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6135 double const y = _region_view->midi_view()->y_position ();
6137 y1 = max (0.0, y1 - y);
6138 y2 = max (0.0, y2 - y);
6140 _region_view->update_vertical_drag_selection (
6143 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6148 MidiVerticalSelectDrag::deselect_things ()
6153 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6154 : RubberbandSelectDrag (e, i)
6160 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6162 if (drag_in_progress) {
6163 /* We just want to select things at the end of the drag, not during it */
6167 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6169 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6171 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6173 _editor->commit_reversible_selection_op ();
6177 EditorRubberbandSelectDrag::deselect_things ()
6179 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6181 _editor->selection->clear_tracks();
6182 _editor->selection->clear_regions();
6183 _editor->selection->clear_points ();
6184 _editor->selection->clear_lines ();
6185 _editor->selection->clear_midi_notes ();
6187 _editor->commit_reversible_selection_op();
6190 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6195 _note[0] = _note[1] = 0;
6198 NoteCreateDrag::~NoteCreateDrag ()
6204 NoteCreateDrag::grid_frames (framepos_t t) const
6207 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6209 grid_beats = Evoral::Beats(1);
6211 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6213 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6214 - _region_view->region_beats_to_region_frames (t_beats);
6219 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6221 Drag::start_grab (event, cursor);
6223 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6224 TempoMap& map (_editor->session()->tempo_map());
6226 const framepos_t pf = _drags->current_pointer_frame ();
6227 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6229 double eqaf = map.exact_qn_at_frame (pf, divisions);
6231 if (divisions != 0) {
6232 bool success = false;
6233 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6235 grid_beats = Evoral::Beats(1);
6238 const double qaf = map.quarter_note_at_frame (pf);
6240 /* Hack so that we always snap to the note that we are over, instead of snapping
6241 to the next one if we're more than halfway through the one we're over.
6244 const double rem = eqaf - qaf;
6246 eqaf -= grid_beats.to_double();
6250 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6251 _note[1] = _note[0];
6253 MidiStreamView* sv = _region_view->midi_stream_view ();
6254 double const x = _editor->sample_to_pixel (_note[0]);
6255 double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6257 _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6258 _drag_rect->set_outline_all ();
6259 _drag_rect->set_outline_color (0xffffff99);
6260 _drag_rect->set_fill_color (0xffffff66);
6264 NoteCreateDrag::motion (GdkEvent* event, bool)
6266 TempoMap& map (_editor->session()->tempo_map());
6267 const framepos_t pf = _drags->current_pointer_frame ();
6268 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6269 double eqaf = map.exact_qn_at_frame (pf, divisions);
6271 if (divisions != 0) {
6272 bool success = false;
6273 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6275 grid_beats = Evoral::Beats(1);
6278 const double qaf = map.quarter_note_at_frame (pf);
6279 /* Hack so that we always snap to the note that we are over, instead of snapping
6280 to the next one if we're more than halfway through the one we're over.
6283 const double rem = eqaf - qaf;
6285 eqaf -= grid_beats.to_double();
6288 eqaf += grid_beats.to_double();
6290 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6292 double const x0 = _editor->sample_to_pixel (_note[0]);
6293 double const x1 = _editor->sample_to_pixel (_note[1]);
6294 _drag_rect->set_x0 (std::min(x0, x1));
6295 _drag_rect->set_x1 (std::max(x0, x1));
6299 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6301 if (!had_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 = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6309 framecnt_t const g = grid_frames (start);
6310 Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6312 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6316 TempoMap& map (_editor->session()->tempo_map());
6317 const double qn_length = map.quarter_note_at_frame (start_sess_rel + length) - map.quarter_note_at_frame (start_sess_rel);
6319 Evoral::Beats qn_length_beats = max (one_tick, Evoral::Beats (qn_length));
6320 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6324 NoteCreateDrag::y_to_region (double y) const
6327 _region_view->get_canvas_group()->canvas_to_item (x, y);
6332 NoteCreateDrag::aborted (bool)
6337 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6342 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6346 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6348 Drag::start_grab (event, cursor);
6352 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6358 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6361 distance = _drags->current_pointer_x() - grab_x();
6362 len = ar->fade_in()->back()->when;
6364 distance = grab_x() - _drags->current_pointer_x();
6365 len = ar->fade_out()->back()->when;
6368 /* how long should it be ? */
6370 new_length = len + _editor->pixel_to_sample (distance);
6372 /* now check with the region that this is legal */
6374 new_length = ar->verify_xfade_bounds (new_length, start);
6377 arv->reset_fade_in_shape_width (ar, new_length);
6379 arv->reset_fade_out_shape_width (ar, new_length);
6384 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6390 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6393 distance = _drags->current_pointer_x() - grab_x();
6394 len = ar->fade_in()->back()->when;
6396 distance = grab_x() - _drags->current_pointer_x();
6397 len = ar->fade_out()->back()->when;
6400 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6402 _editor->begin_reversible_command ("xfade trim");
6403 ar->playlist()->clear_owned_changes ();
6406 ar->set_fade_in_length (new_length);
6408 ar->set_fade_out_length (new_length);
6411 /* Adjusting the xfade may affect other regions in the playlist, so we need
6412 to get undo Commands from the whole playlist rather than just the
6416 vector<Command*> cmds;
6417 ar->playlist()->rdiff (cmds);
6418 _editor->session()->add_commands (cmds);
6419 _editor->commit_reversible_command ();
6424 CrossfadeEdgeDrag::aborted (bool)
6427 // arv->redraw_start_xfade ();
6429 // arv->redraw_end_xfade ();
6433 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6434 : Drag (e, item, true)
6435 , line (new EditorCursor (*e))
6437 line->set_position (pos);
6441 RegionCutDrag::~RegionCutDrag ()
6447 RegionCutDrag::motion (GdkEvent*, bool)
6449 framepos_t where = _drags->current_pointer_frame();
6450 _editor->snap_to (where);
6452 line->set_position (where);
6456 RegionCutDrag::finished (GdkEvent* event, bool)
6458 _editor->get_track_canvas()->canvas()->re_enter();
6460 framepos_t pos = _drags->current_pointer_frame();
6464 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6470 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6474 RegionCutDrag::aborted (bool)
6478 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6479 : Drag (e, item, true)
6484 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6486 Drag::start_grab (event, c);
6488 framepos_t where = _editor->canvas_event_sample(event);
6490 _editor->_dragging_playhead = true;
6492 _editor->playhead_cursor->set_position (where);
6496 RulerZoomDrag::motion (GdkEvent* event, bool)
6498 framepos_t where = _editor->canvas_event_sample(event);
6500 _editor->playhead_cursor->set_position (where);
6502 const double movement_limit = 20.0;
6503 const double scale = 1.08;
6504 const double y_delta = last_pointer_y() - current_pointer_y();
6506 if (y_delta > 0 && y_delta < movement_limit) {
6507 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6508 } else if (y_delta < 0 && y_delta > -movement_limit) {
6509 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6514 RulerZoomDrag::finished (GdkEvent*, bool)
6516 _editor->_dragging_playhead = false;
6518 Session* s = _editor->session ();
6520 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6521 _editor->_pending_locate_request = true;
6527 RulerZoomDrag::aborted (bool)