2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
31 #include "gtkmm2ext/utils.h"
33 #include "ardour/audioengine.h"
34 #include "ardour/audioregion.h"
35 #include "ardour/audio_track.h"
36 #include "ardour/dB.h"
37 #include "ardour/midi_region.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/operations.h"
40 #include "ardour/region_factory.h"
41 #include "ardour/session.h"
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
49 #include "audio_region_view.h"
50 #include "automation_region_view.h"
51 #include "midi_region_view.h"
52 #include "ardour_ui.h"
53 #include "gui_thread.h"
54 #include "control_point.h"
55 #include "region_gain_line.h"
56 #include "editor_drag.h"
57 #include "audio_time_axis.h"
58 #include "midi_time_axis.h"
59 #include "selection.h"
60 #include "midi_selection.h"
61 #include "automation_time_axis.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "ui_config.h"
68 #include "verbose_cursor.h"
71 using namespace ARDOUR;
74 using namespace Gtkmm2ext;
75 using namespace Editing;
76 using namespace ArdourCanvas;
78 using Gtkmm2ext::Keyboard;
80 double ControlPointDrag::_zero_gain_fraction = -1.0;
82 DragManager::DragManager (Editor* e)
85 , _current_pointer_x (0.0)
86 , _current_pointer_y (0.0)
87 , _current_pointer_frame (0)
88 , _old_follow_playhead (false)
92 DragManager::~DragManager ()
97 /** Call abort for each active drag */
103 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108 if (!_drags.empty ()) {
109 _editor->set_follow_playhead (_old_follow_playhead, false);
113 _editor->abort_reversible_command();
119 DragManager::add (Drag* d)
121 d->set_manager (this);
122 _drags.push_back (d);
126 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
128 d->set_manager (this);
129 _drags.push_back (d);
134 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
136 /* Prevent follow playhead during the drag to be nice to the user */
137 _old_follow_playhead = _editor->follow_playhead ();
138 _editor->set_follow_playhead (false);
140 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
142 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
143 (*i)->start_grab (e, c);
147 /** Call end_grab for each active drag.
148 * @return true if any drag reported movement having occurred.
151 DragManager::end_grab (GdkEvent* e)
156 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
157 bool const t = (*i)->end_grab (e);
168 _editor->set_follow_playhead (_old_follow_playhead, false);
174 DragManager::mark_double_click ()
176 for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
177 (*i)->set_double_click (true);
182 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
186 /* calling this implies that we expect the event to have canvas
189 * Can we guarantee that this is true?
192 _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
194 for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
195 bool const t = (*i)->motion_handler (e, from_autoscroll);
196 /* run all handlers; return true if at least one of them
197 returns true (indicating that the event has been handled).
209 DragManager::have_item (ArdourCanvas::Item* i) const
211 list<Drag*>::const_iterator j = _drags.begin ();
212 while (j != _drags.end() && (*j)->item () != i) {
216 return j != _drags.end ();
219 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
223 , _pointer_frame_offset (0)
224 , _x_constrained (false)
225 , _y_constrained (false)
226 , _was_rolling (false)
227 , _trackview_only (trackview_only)
228 , _move_threshold_passed (false)
229 , _starting_point_passed (false)
230 , _initially_vertical (false)
231 , _was_double_click (false)
234 , _last_pointer_x (0.0)
235 , _last_pointer_y (0.0)
236 , _raw_grab_frame (0)
238 , _last_pointer_frame (0)
240 , _constraint_pressed (false)
246 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
252 _cursor_ctx = CursorContext::create (*_editor, cursor);
254 _cursor_ctx->change (cursor);
261 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
264 /* we set up x/y dragging constraints on first move */
265 _constraint_pressed = ArdourKeyboard::indicates_constraint (event->button.state);
267 _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
269 setup_pointer_frame_offset ();
270 _grab_frame = adjusted_frame (_raw_grab_frame, event);
271 _last_pointer_frame = _grab_frame;
272 _last_pointer_x = _grab_x;
274 if (_trackview_only) {
275 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
278 _last_pointer_y = _grab_y;
282 if (!_editor->cursors()->is_invalid (cursor)) {
283 /* CAIROCANVAS need a variant here that passes *cursor */
284 _cursor_ctx = CursorContext::create (*_editor, cursor);
287 if (_editor->session() && _editor->session()->transport_rolling()) {
290 _was_rolling = false;
293 switch (_editor->snap_type()) {
294 case SnapToRegionStart:
295 case SnapToRegionEnd:
296 case SnapToRegionSync:
297 case SnapToRegionBoundary:
298 _editor->build_region_boundary_cache ();
305 /** Call to end a drag `successfully'. Ungrabs item and calls
306 * subclass' finished() method.
308 * @param event GDK event, or 0.
309 * @return true if some movement occurred, otherwise false.
312 Drag::end_grab (GdkEvent* event)
314 _editor->stop_canvas_autoscroll ();
318 finished (event, _move_threshold_passed);
320 _editor->verbose_cursor()->hide ();
323 return _move_threshold_passed;
327 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
331 if (f > _pointer_frame_offset) {
332 pos = f - _pointer_frame_offset;
336 _editor->snap_to_with_modifier (pos, event);
343 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
345 return adjusted_frame (_drags->current_pointer_frame (), event, snap);
349 Drag::snap_delta (guint state) const
351 if (ArdourKeyboard::indicates_snap_delta (state)) {
359 Drag::current_pointer_x() const
361 return _drags->current_pointer_x ();
365 Drag::current_pointer_y () const
367 if (!_trackview_only) {
368 return _drags->current_pointer_y ();
371 return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
375 Drag::setup_snap_delta (framepos_t pos)
377 framepos_t temp = pos;
378 _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
379 _snap_delta = temp - pos;
383 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
385 /* check to see if we have moved in any way that matters since the last motion event */
386 if (_move_threshold_passed &&
387 (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
388 (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
392 pair<framecnt_t, int> const threshold = move_threshold ();
394 bool const old_move_threshold_passed = _move_threshold_passed;
396 if (!_move_threshold_passed) {
398 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
399 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
401 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
404 if (active (_editor->mouse_mode) && _move_threshold_passed) {
406 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
408 if (old_move_threshold_passed != _move_threshold_passed) {
412 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
413 _initially_vertical = true;
415 _initially_vertical = false;
417 /** check constraints for this drag.
418 * Note that the current convention is to use "contains" for
419 * key modifiers during motion and "equals" when initiating a drag.
420 * In this case we haven't moved yet, so "equals" applies here.
422 if (Config->get_edit_mode() != Lock) {
423 if (event->motion.state & Gdk::BUTTON2_MASK) {
424 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
425 if (_constraint_pressed) {
426 _x_constrained = false;
427 _y_constrained = true;
429 _x_constrained = true;
430 _y_constrained = false;
432 } else if (_constraint_pressed) {
433 // if dragging normally, the motion is constrained to the first direction of movement.
434 if (_initially_vertical) {
435 _x_constrained = true;
436 _y_constrained = false;
438 _x_constrained = false;
439 _y_constrained = true;
443 if (event->button.state & Gdk::BUTTON2_MASK) {
444 _x_constrained = false;
446 _x_constrained = true;
448 _y_constrained = false;
452 if (!from_autoscroll) {
453 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
456 if (!_editor->autoscroll_active() || from_autoscroll) {
459 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
461 motion (event, first_move && !_starting_point_passed);
463 if (first_move && !_starting_point_passed) {
464 _starting_point_passed = true;
467 _last_pointer_x = _drags->current_pointer_x ();
468 _last_pointer_y = current_pointer_y ();
469 _last_pointer_frame = adjusted_current_frame (event, false);
479 /** Call to abort a drag. Ungrabs item and calls subclass's aborted () */
487 aborted (_move_threshold_passed);
489 _editor->stop_canvas_autoscroll ();
490 _editor->verbose_cursor()->hide ();
494 Drag::show_verbose_cursor_time (framepos_t frame)
496 _editor->verbose_cursor()->set_time (frame);
497 _editor->verbose_cursor()->show ();
501 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
503 _editor->verbose_cursor()->set_duration (start, end);
504 _editor->verbose_cursor()->show ();
508 Drag::show_verbose_cursor_text (string const & text)
510 _editor->verbose_cursor()->set (text);
511 _editor->verbose_cursor()->show ();
514 boost::shared_ptr<Region>
515 Drag::add_midi_region (MidiTimeAxisView* view, bool commit, const int32_t sub_num)
517 if (_editor->session()) {
518 const TempoMap& map (_editor->session()->tempo_map());
519 framecnt_t pos = grab_frame();
520 /* not that the frame rate used here can be affected by pull up/down which
523 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
524 return view->add_region (grab_frame(), len, commit, sub_num);
527 return boost::shared_ptr<Region>();
530 struct PresentationInfoTimeAxisViewSorter {
531 bool operator() (TimeAxisView* a, TimeAxisView* b) {
532 return a->stripable()->presentation_info().order() < b->stripable()->presentation_info().order();
536 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
541 _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
543 /* Make a list of tracks to refer to during the drag; we include hidden tracks,
544 as some of the regions we are dragging may be on such tracks.
547 TrackViewList track_views = _editor->track_views;
548 track_views.sort (PresentationInfoTimeAxisViewSorter ());
550 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
551 _time_axis_views.push_back (*i);
553 TimeAxisView::Children children_list = (*i)->get_child_list ();
554 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
555 _time_axis_views.push_back (j->get());
559 /* the list of views can be empty at this point if this is a region list-insert drag
562 for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
563 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
566 RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
570 RegionDrag::region_going_away (RegionView* v)
572 list<DraggingView>::iterator i = _views.begin ();
573 while (i != _views.end() && i->view != v) {
577 if (i != _views.end()) {
582 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
583 * or -1 if it is not found.
586 RegionDrag::find_time_axis_view (TimeAxisView* t) const
589 int const N = _time_axis_views.size ();
590 while (i < N && _time_axis_views[i] != t) {
594 if (_time_axis_views[i] != t) {
601 /** determines if @pos is the closest frame to an exact musical division when using SnapMagnetic.
602 * (SnapMagnetic may snap to an exact division or no division at all).
603 * this is a hotfix for musical region position/trim. we should really
604 * deliver musical divisors when snapping magnetically to avoid this.
607 RegionDrag::current_music_divisor (framepos_t pos, int32_t button_state)
609 int32_t divisions = _editor->get_grid_music_divisions (button_state);
611 if (_editor->snap_mode() == Editing::SnapMagnetic && !ArdourKeyboard::indicates_snap (button_state)) {
612 TempoMap& tmap (_editor->session()->tempo_map());
613 const framepos_t exact_qn_pos = tmap.frame_at_quarter_note (tmap.exact_qn_at_frame (pos, divisions));
615 if (pos != exact_qn_pos) {
616 /* magnetic has not snapped */
624 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
625 : RegionDrag (e, i, p, v)
627 , _ignore_video_lock (false)
629 , _last_pointer_time_axis_view (0)
630 , _last_pointer_layer (0)
635 DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
639 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
641 Drag::start_grab (event, cursor);
642 setup_snap_delta (_last_frame_position);
644 show_verbose_cursor_time (_last_frame_position);
646 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
648 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
649 assert(_last_pointer_time_axis_view >= 0);
650 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
653 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
654 _ignore_video_lock = true;
658 /* cross track dragging seems broken here. disabled for now. */
659 _y_constrained = true;
664 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
666 /* compute the amount of pointer motion in frames, and where
667 the region would be if we moved it by that much.
669 *pending_region_position = adjusted_frame (_drags->current_pointer_frame (), event, false);
671 framepos_t sync_frame;
672 framecnt_t sync_offset;
675 sync_offset = _primary->region()->sync_offset (sync_dir);
677 /* we don't handle a sync point that lies before zero.
679 if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
681 framecnt_t const sd = snap_delta (event->button.state);
682 sync_frame = *pending_region_position + (sync_dir * sync_offset) + sd;
684 _editor->snap_to_with_modifier (sync_frame, event);
686 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - sd;
689 *pending_region_position = _last_frame_position;
692 if (*pending_region_position > max_framepos - _primary->region()->length()) {
693 *pending_region_position = _last_frame_position;
698 bool const x_move_allowed = !_x_constrained;
700 if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
702 /* x movement since last time (in pixels) */
703 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
705 /* total x movement */
706 framecnt_t total_dx = *pending_region_position;
707 if (regions_came_from_canvas()) {
708 total_dx = total_dx - grab_frame ();
711 /* check that no regions have gone off the start of the session */
712 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
713 if ((i->view->region()->position() + total_dx) < 0) {
715 *pending_region_position = _last_frame_position;
726 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
732 const int tavsize = _time_axis_views.size();
733 const int dt = delta > 0 ? +1 : -1;
735 int target = start + delta - skip;
737 assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
738 assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
740 while (current >= 0 && current != target) {
742 if (current < 0 && dt < 0) {
745 if (current >= tavsize && dt > 0) {
748 if (current < 0 || current >= tavsize) {
752 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
753 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
757 if (distance_only && current == start + delta) {
765 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
767 if (_y_constrained) {
771 const int tavsize = _time_axis_views.size();
772 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
773 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
774 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
776 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
777 /* already in the drop zone */
778 if (delta_track >= 0) {
779 /* downward motion - OK if others are still not in the dropzone */
788 } else if (n >= tavsize) {
789 /* downward motion into drop zone. That's fine. */
793 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
794 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
795 /* not a track, or the wrong type */
799 double const l = i->layer + delta_layer;
801 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
802 mode to allow the user to place a region below another on layer 0.
804 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
805 /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
806 If it has, the layers will be munged later anyway, so it's ok.
812 /* all regions being dragged are ok with this change */
816 struct DraggingViewSorter {
817 bool operator() (const DraggingView& a, const DraggingView& b) {
818 return a.time_axis_view < b.time_axis_view;
823 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
825 double delta_layer = 0;
826 int delta_time_axis_view = 0;
827 int current_pointer_time_axis_view = -1;
829 assert (!_views.empty ());
831 /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
833 /* Find the TimeAxisView that the pointer is now over */
834 const double cur_y = current_pointer_y ();
835 pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
836 TimeAxisView* tv = r.first;
838 if (!tv && cur_y < 0) {
839 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
843 /* find drop-zone y-position */
844 Coord last_track_bottom_edge;
845 last_track_bottom_edge = 0;
846 for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
847 if (!(*t)->hidden()) {
848 last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
853 if (tv && tv->view()) {
854 /* the mouse is over a track */
855 double layer = r.second;
857 if (first_move && tv->view()->layer_display() == Stacked) {
858 tv->view()->set_layer_display (Expanded);
861 /* Here's the current pointer position in terms of time axis view and layer */
862 current_pointer_time_axis_view = find_time_axis_view (tv);
863 assert(current_pointer_time_axis_view >= 0);
865 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
867 /* Work out the change in y */
869 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
870 if (!rtv || !rtv->is_track()) {
871 /* ignore non-tracks early on. we can't move any regions on them */
872 } else if (_last_pointer_time_axis_view < 0) {
873 /* Was in the drop-zone, now over a track.
874 * Hence it must be an upward move (from the bottom)
876 * track_index is still -1, so delta must be set to
877 * move up the correct number of tracks from the bottom.
879 * This is necessary because steps may be skipped if
880 * the bottom-most track is not a valid target and/or
881 * if there are hidden tracks at the bottom.
882 * Hence the initial offset (_ddropzone) as well as the
883 * last valid pointer position (_pdropzone) need to be
884 * taken into account.
886 delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
888 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
891 /* TODO needs adjustment per DraggingView,
893 * e.g. select one region on the top-layer of a track
894 * and one region which is at the bottom-layer of another track
897 * Indicated drop-zones and layering is wrong.
898 * and may infer additional layers on the target-track
899 * (depending how many layers the original track had).
901 * Or select two regions (different layers) on a same track,
902 * move across a non-layer track.. -> layering info is lost.
903 * on drop either of the regions may be on top.
905 * Proposed solution: screw it :) well,
906 * don't use delta_layer, use an absolute value
907 * 1) remember DraggingView's layer as float 0..1 [current layer / all layers of source]
908 * 2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
909 * 3) iterate over all DraggingView, find the one that is over the track with most layers
910 * 4) proportionally scale layer to layers available on target
912 delta_layer = current_pointer_layer - _last_pointer_layer;
915 /* for automation lanes, there is a TimeAxisView but no ->view()
916 * if (!tv) -> dropzone
918 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
919 /* Moving into the drop-zone.. */
920 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
921 /* delta_time_axis_view may not be sufficient to move into the DZ
922 * the mouse may enter it, but it may not be a valid move due to
925 * -> remember the delta needed to move into the dropzone
927 _ddropzone = delta_time_axis_view;
928 /* ..but subtract hidden tracks (or routes) at the bottom.
929 * we silently move mover them
931 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
932 - _time_axis_views.size();
934 else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
935 /* move around inside the zone.
936 * This allows to move further down until all regions are in the zone.
938 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
939 assert(ptr_y >= last_track_bottom_edge);
940 assert(_ddropzone > 0);
942 /* calculate mouse position in 'tracks' below last track. */
943 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
944 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
946 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
948 delta_time_axis_view = dzpos - _pdropzone;
949 } else if (dzpos < _pdropzone && _ndropzone > 0) {
950 // move up inside the DZ
951 delta_time_axis_view = dzpos - _pdropzone;
955 /* Work out the change in x */
956 framepos_t pending_region_position;
957 double const x_delta = compute_x_delta (event, &pending_region_position);
958 _last_frame_position = pending_region_position;
960 /* calculate hidden tracks in current y-axis delta */
962 if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
963 /* The mouse is more than one track below the dropzone.
964 * distance calculation is not needed (and would not work, either
965 * because the dropzone is "packed").
967 * Except when [partially] moving regions out of dropzone in a large step.
968 * (the mouse may or may not remain in the DZ)
969 * Hidden tracks at the bottom of the TAV need to be skipped.
971 * This also handles the case if the mouse entered the DZ
972 * in a large step (exessive delta), either due to fast-movement,
973 * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
975 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
976 const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
978 delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
979 -_time_axis_views.size() - dt;
982 else if (_last_pointer_time_axis_view < 0) {
983 /* Moving out of the zone. Check for hidden tracks at the bottom. */
984 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
985 -_time_axis_views.size() - delta_time_axis_view;
987 /* calculate hidden tracks that are skipped by the pointer movement */
988 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
989 - _last_pointer_time_axis_view
990 - delta_time_axis_view;
993 /* Verify change in y */
994 if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
995 /* this y movement is not allowed, so do no y movement this time */
996 delta_time_axis_view = 0;
1001 if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
1002 /* haven't reached next snap point, and we're not switching
1003 trackviews nor layers. nothing to do.
1008 typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
1009 PlaylistDropzoneMap playlist_dropzone_map;
1010 _ndropzone = 0; // number of elements currently in the dropzone
1013 /* sort views by time_axis.
1014 * This retains track order in the dropzone, regardless
1015 * of actual selection order
1017 _views.sort (DraggingViewSorter());
1019 /* count number of distinct tracks of all regions
1020 * being dragged, used for dropzone.
1022 int prev_track = -1;
1023 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1024 if (i->time_axis_view != prev_track) {
1025 prev_track = i->time_axis_view;
1031 _views.back().time_axis_view -
1032 _views.front().time_axis_view;
1034 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1035 - _views.back().time_axis_view;
1037 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1041 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1043 RegionView* rv = i->view;
1048 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1055 /* reparent the regionview into a group above all
1059 ArdourCanvas::Item* rvg = rv->get_canvas_group();
1060 Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1061 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1062 rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1063 /* move the item so that it continues to appear at the
1064 same location now that its parent has changed.
1066 rvg->move (rv_canvas_offset - dmg_canvas_offset);
1069 /* If we have moved tracks, we'll fudge the layer delta so that the
1070 region gets moved back onto layer 0 on its new track; this avoids
1071 confusion when dragging regions from non-zero layers onto different
1074 double this_delta_layer = delta_layer;
1075 if (delta_time_axis_view != 0) {
1076 this_delta_layer = - i->layer;
1079 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1081 int track_index = i->time_axis_view + this_delta_time_axis_view;
1082 assert(track_index >= 0);
1084 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1085 /* Track is in the Dropzone */
1087 i->time_axis_view = track_index;
1088 assert(i->time_axis_view >= (int) _time_axis_views.size());
1091 double yposition = 0;
1092 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1093 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1096 /* store index of each new playlist as a negative count, starting at -1 */
1098 if (pdz == playlist_dropzone_map.end()) {
1099 /* compute where this new track (which doesn't exist yet) will live
1102 yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1104 /* How high is this region view ? */
1106 boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1107 ArdourCanvas::Rect bbox;
1110 bbox = obbox.get ();
1113 last_track_bottom_edge += bbox.height();
1115 playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1118 yposition = pdz->second;
1121 /* values are zero or negative, hence the use of min() */
1122 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1125 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1127 mrv->apply_note_range (60, 71, true);
1131 /* The TimeAxisView that this region is now over */
1132 TimeAxisView* current_tv = _time_axis_views[track_index];
1134 /* Ensure it is moved from stacked -> expanded if appropriate */
1135 if (current_tv->view()->layer_display() == Stacked) {
1136 current_tv->view()->set_layer_display (Expanded);
1139 /* We're only allowed to go -ve in layer on Expanded views */
1140 if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1141 this_delta_layer = - i->layer;
1145 rv->set_height (current_tv->view()->child_height ());
1147 /* Update show/hidden status as the region view may have come from a hidden track,
1148 or have moved to one.
1150 if (current_tv->hidden ()) {
1151 rv->get_canvas_group()->hide ();
1153 rv->get_canvas_group()->show ();
1156 /* Update the DraggingView */
1157 i->time_axis_view = track_index;
1158 i->layer += this_delta_layer;
1161 _editor->mouse_brush_insert_region (rv, pending_region_position);
1165 /* Get the y coordinate of the top of the track that this region is now over */
1166 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1168 /* And adjust for the layer that it should be on */
1169 StreamView* cv = current_tv->view ();
1170 switch (cv->layer_display ()) {
1174 track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1177 track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1181 /* need to get the parent of the regionview
1182 * canvas group and get its position in
1183 * equivalent coordinate space as the trackview
1184 * we are now dragging over.
1187 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1191 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1193 MidiStreamView* msv;
1194 if ((msv = dynamic_cast <MidiStreamView*> (current_tv->view())) != 0) {
1195 mrv->apply_note_range (msv->lowest_note(), msv->highest_note(), true);
1200 /* Now move the region view */
1201 rv->move (x_delta, y_delta);
1203 } /* foreach region */
1205 _total_x_delta += x_delta;
1207 if (x_delta != 0 && !_brushing) {
1208 show_verbose_cursor_time (_last_frame_position);
1211 /* keep track of pointer movement */
1213 /* the pointer is currently over a time axis view */
1215 if (_last_pointer_time_axis_view < 0) {
1216 /* last motion event was not over a time axis view
1217 * or last y-movement out of the dropzone was not valid
1220 if (delta_time_axis_view < 0) {
1221 /* in the drop zone, moving up */
1223 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1224 * We do not use negative _last_pointer_time_axis_view because
1225 * the dropzone is "packed" (the actual track offset is ignored)
1227 * As opposed to the actual number
1228 * of elements in the dropzone (_ndropzone)
1229 * _pdropzone is not constrained. This is necessary
1230 * to allow moving multiple regions with y-distance
1233 * There can be 0 elements in the dropzone,
1234 * even though the drag-pointer is inside the DZ.
1237 * [ Audio-track, Midi-track, Audio-track, DZ ]
1238 * move regions from both audio tracks at the same time into the
1239 * DZ by grabbing the region in the bottom track.
1241 assert(current_pointer_time_axis_view >= 0);
1242 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1246 /* only move out of the zone if the movement is OK */
1247 if (_pdropzone == 0 && delta_time_axis_view != 0) {
1248 assert(delta_time_axis_view < 0);
1249 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1250 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1251 * the current position can be calculated as follows:
1253 // a well placed oofus attack can still throw this off.
1254 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1255 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1258 /* last motion event was also over a time axis view */
1259 _last_pointer_time_axis_view += delta_time_axis_view;
1260 assert(_last_pointer_time_axis_view >= 0);
1265 /* the pointer is not over a time axis view */
1266 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1267 _pdropzone += delta_time_axis_view - delta_skip;
1268 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1271 _last_pointer_layer += delta_layer;
1275 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1277 if (_copy && first_move) {
1278 if (_x_constrained && !_brushing) {
1279 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1280 } else if (!_brushing) {
1281 _editor->begin_reversible_command (Operations::region_copy);
1282 } else if (_brushing) {
1283 _editor->begin_reversible_command (Operations::drag_region_brush);
1285 /* duplicate the regionview(s) and region(s) */
1287 list<DraggingView> new_regionviews;
1289 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1291 RegionView* rv = i->view;
1292 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1293 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1295 const boost::shared_ptr<const Region> original = rv->region();
1296 boost::shared_ptr<Region> region_copy;
1298 if (rv == _primary) {
1299 region_copy = RegionFactory::create (original, true
1300 , current_music_divisor (original->position(), event->button.state));
1302 region_copy = RegionFactory::create (original, true, 0);
1305 /* need to set this so that the drop zone code can work. This doesn't
1306 actually put the region into the playlist, but just sets a weak pointer
1309 region_copy->set_playlist (original->playlist());
1313 boost::shared_ptr<AudioRegion> audioregion_copy
1314 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1316 nrv = new AudioRegionView (*arv, audioregion_copy);
1318 boost::shared_ptr<MidiRegion> midiregion_copy
1319 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1320 nrv = new MidiRegionView (*mrv, midiregion_copy);
1325 nrv->get_canvas_group()->show ();
1326 new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1328 /* swap _primary to the copy */
1330 if (rv == _primary) {
1334 /* ..and deselect the one we copied */
1336 rv->set_selected (false);
1339 if (!new_regionviews.empty()) {
1341 /* reflect the fact that we are dragging the copies */
1343 _views = new_regionviews;
1345 swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1348 } else if (!_copy && first_move) {
1349 if (_x_constrained && !_brushing) {
1350 _editor->begin_reversible_command (_("fixed time region drag"));
1351 } else if (!_brushing) {
1352 _editor->begin_reversible_command (Operations::region_drag);
1353 } else if (_brushing) {
1354 _editor->begin_reversible_command (Operations::drag_region_brush);
1357 RegionMotionDrag::motion (event, first_move);
1361 RegionMotionDrag::finished (GdkEvent *, bool)
1363 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1364 if (!(*i)->view()) {
1368 if ((*i)->view()->layer_display() == Expanded) {
1369 (*i)->view()->set_layer_display (Stacked);
1375 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1377 RegionMotionDrag::finished (ev, movement_occurred);
1379 if (!movement_occurred) {
1383 if (was_double_click() && !_views.empty()) {
1384 DraggingView dv = _views.front();
1385 _editor->edit_region (dv.view);
1391 assert (!_views.empty ());
1393 /* We might have hidden region views so that they weren't visible during the drag
1394 (when they have been reparented). Now everything can be shown again, as region
1395 views are back in their track parent groups.
1397 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1398 i->view->get_canvas_group()->show ();
1401 bool const changed_position = (_last_frame_position != _primary->region()->position());
1402 bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1403 framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1425 _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1429 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1431 /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1434 TimeAxisView* tav = 0;
1436 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1437 list<boost::shared_ptr<AudioTrack> > audio_tracks;
1438 uint32_t output_chan = region->n_channels();
1439 if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1440 output_chan = _editor->session()->master_out()->n_inputs().n_audio();
1442 audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, 0, 1, region->name(), PresentationInfo::max_order);
1443 tav =_editor->axis_view_from_stripable (audio_tracks.front());
1445 ChanCount one_midi_port (DataType::MIDI, 1);
1446 list<boost::shared_ptr<MidiTrack> > midi_tracks;
1447 midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(),
1448 (ARDOUR::Plugin::PresetRecord*) 0,
1449 (ARDOUR::RouteGroup*) 0, 1, region->name(), PresentationInfo::max_order);
1450 tav = _editor->axis_view_from_stripable (midi_tracks.front());
1454 tav->set_height (original->current_height());
1457 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1460 return dynamic_cast<RouteTimeAxisView*> (tav);
1464 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta, int32_t const ev_state)
1466 RegionSelection new_views;
1467 PlaylistSet modified_playlists;
1468 RouteTimeAxisView* new_time_axis_view = 0;
1470 int32_t divisor = current_music_divisor (_primary->region()->position() - drag_delta, ev_state);
1471 TempoMap& tmap (_editor->session()->tempo_map());
1472 double qn_delta = _primary->region()->quarter_note() - tmap.exact_qn_at_frame (_primary->region()->position() - drag_delta, divisor);
1475 /* all changes were made during motion event handlers */
1477 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1481 _editor->commit_reversible_command ();
1485 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1486 PlaylistMapping playlist_mapping;
1488 /* insert the regions into their new playlists */
1489 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1491 RouteTimeAxisView* dest_rtv = 0;
1493 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1499 if (changed_position && !_x_constrained) {
1500 where = i->view->region()->position() - drag_delta;
1502 where = i->view->region()->position();
1505 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1506 /* dragged to drop zone */
1508 PlaylistMapping::iterator pm;
1510 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1511 /* first region from this original playlist: create a new track */
1512 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1513 if(!new_time_axis_view) {
1517 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1518 dest_rtv = new_time_axis_view;
1520 /* we already created a new track for regions from this playlist, use it */
1521 dest_rtv = pm->second;
1524 /* destination time axis view is the one we dragged to */
1525 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1528 if (dest_rtv != 0) {
1529 RegionView* new_view;
1530 if (i->view == _primary) {
1531 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1532 modified_playlists, current_music_divisor (where, ev_state));
1534 if (i->view->region()->position_lock_style() == AudioTime) {
1535 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1536 modified_playlists, 0);
1538 where = tmap.frame_at_quarter_note (i->view->region()->quarter_note() - qn_delta);
1539 new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where,
1540 modified_playlists, 0);
1544 if (new_view != 0) {
1545 new_views.push_back (new_view);
1549 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1550 since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1553 list<DraggingView>::const_iterator next = i;
1559 /* If we've created new regions either by copying or moving
1560 to a new track, we want to replace the old selection with the new ones
1563 if (new_views.size() > 0) {
1564 _editor->selection->set (new_views);
1567 /* write commands for the accumulated diffs for all our modified playlists */
1568 add_stateful_diff_commands_for_playlists (modified_playlists);
1570 _editor->commit_reversible_command ();
1574 RegionMoveDrag::finished_no_copy (
1575 bool const changed_position,
1576 bool const changed_tracks,
1577 framecnt_t const drag_delta,
1578 int32_t const ev_state
1581 RegionSelection new_views;
1582 PlaylistSet modified_playlists;
1583 PlaylistSet frozen_playlists;
1584 set<RouteTimeAxisView*> views_to_update;
1585 RouteTimeAxisView* new_time_axis_view = 0;
1587 typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1588 PlaylistMapping playlist_mapping;
1590 int32_t divisor = current_music_divisor (_primary->region()->position() - drag_delta, ev_state);
1591 TempoMap& tmap (_editor->session()->tempo_map());
1592 double qn_delta = _primary->region()->quarter_note() - tmap.exact_qn_at_frame (_primary->region()->position() - drag_delta, divisor);
1594 std::set<boost::shared_ptr<const Region> > uniq;
1595 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1597 RegionView* rv = i->view;
1598 RouteTimeAxisView* dest_rtv = 0;
1600 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1605 if (uniq.find (rv->region()) != uniq.end()) {
1606 /* prevent duplicate moves when selecting regions from shared playlists */
1610 uniq.insert(rv->region());
1612 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1613 /* dragged to drop zone */
1615 PlaylistMapping::iterator pm;
1617 if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1618 /* first region from this original playlist: create a new track */
1619 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1620 if(!new_time_axis_view) { // New track creation failed
1624 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1625 dest_rtv = new_time_axis_view;
1627 /* we already created a new track for regions from this playlist, use it */
1628 dest_rtv = pm->second;
1632 /* destination time axis view is the one we dragged to */
1633 dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1638 double const dest_layer = i->layer;
1640 views_to_update.insert (dest_rtv);
1644 if (changed_position && !_x_constrained) {
1645 where = rv->region()->position() - drag_delta;
1647 where = rv->region()->position();
1650 if (changed_tracks) {
1652 /* insert into new playlist */
1653 RegionView* new_view;
1654 if (rv == _primary) {
1655 new_view = insert_region_into_playlist (
1656 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1657 modified_playlists, current_music_divisor (where, ev_state)
1660 if (rv->region()->position_lock_style() == AudioTime) {
1662 new_view = insert_region_into_playlist (
1663 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1664 modified_playlists, 0
1667 where = tmap.frame_at_quarter_note (rv->region()->quarter_note() - qn_delta);
1668 new_view = insert_region_into_playlist (
1669 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1670 modified_playlists, 0
1675 if (new_view == 0) {
1680 new_views.push_back (new_view);
1682 /* remove from old playlist */
1684 /* the region that used to be in the old playlist is not
1685 moved to the new one - we use a copy of it. as a result,
1686 any existing editor for the region should no longer be
1689 rv->hide_region_editor();
1692 remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1696 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1698 /* this movement may result in a crossfade being modified, or a layering change,
1699 so we need to get undo data from the playlist as well as the region.
1702 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1704 playlist->clear_changes ();
1707 rv->region()->clear_changes ();
1710 motion on the same track. plonk the previously reparented region
1711 back to its original canvas group (its streamview).
1712 No need to do anything for copies as they are fake regions which will be deleted.
1715 rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1716 rv->get_canvas_group()->set_y_position (i->initial_y);
1719 /* just change the model */
1720 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1721 playlist->set_layer (rv->region(), dest_layer);
1724 /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1726 r = frozen_playlists.insert (playlist);
1729 playlist->freeze ();
1731 if (rv == _primary) {
1732 rv->region()->set_position (where, current_music_divisor (where, ev_state));
1734 if (rv->region()->position_lock_style() == AudioTime) {
1735 rv->region()->set_position (where, 0);
1737 rv->region()->set_position (tmap.frame_at_quarter_note (rv->region()->quarter_note() - qn_delta), 0);
1741 _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1744 if (changed_tracks) {
1746 /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1747 was selected in all of them, then removing it from a playlist will have removed all
1748 trace of it from _views (i.e. there were N regions selected, we removed 1,
1749 but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1750 corresponding regionview, and _views is now empty).
1752 This could have invalidated any and all iterators into _views.
1754 The heuristic we use here is: if the region selection is empty, break out of the loop
1755 here. if the region selection is not empty, then restart the loop because we know that
1756 we must have removed at least the region(view) we've just been working on as well as any
1757 that we processed on previous iterations.
1759 EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1760 we can just iterate.
1764 if (_views.empty()) {
1775 /* If we've created new regions either by copying or moving
1776 to a new track, we want to replace the old selection with the new ones
1779 if (new_views.size() > 0) {
1780 _editor->selection->set (new_views);
1783 for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1787 /* write commands for the accumulated diffs for all our modified playlists */
1788 add_stateful_diff_commands_for_playlists (modified_playlists);
1789 /* applies to _brushing */
1790 _editor->commit_reversible_command ();
1792 /* We have futzed with the layering of canvas items on our streamviews.
1793 If any region changed layer, this will have resulted in the stream
1794 views being asked to set up their region views, and all will be well.
1795 If not, we might now have badly-ordered region views. Ask the StreamViews
1796 involved to sort themselves out, just in case.
1799 for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1800 (*i)->view()->playlist_layered ((*i)->track ());
1804 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1805 * @param region Region to remove.
1806 * @param playlist playlist To remove from.
1807 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1808 * that clear_changes () is only called once per playlist.
1811 RegionMoveDrag::remove_region_from_playlist (
1812 boost::shared_ptr<Region> region,
1813 boost::shared_ptr<Playlist> playlist,
1814 PlaylistSet& modified_playlists
1817 pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1820 playlist->clear_changes ();
1823 playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1827 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1828 * clearing the playlist's diff history first if necessary.
1829 * @param region Region to insert.
1830 * @param dest_rtv Destination RouteTimeAxisView.
1831 * @param dest_layer Destination layer.
1832 * @param where Destination position.
1833 * @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1834 * that clear_changes () is only called once per playlist.
1835 * @return New RegionView, or 0 if no insert was performed.
1838 RegionMoveDrag::insert_region_into_playlist (
1839 boost::shared_ptr<Region> region,
1840 RouteTimeAxisView* dest_rtv,
1843 PlaylistSet& modified_playlists,
1844 const int32_t sub_num
1847 boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1848 if (!dest_playlist) {
1852 /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1853 _new_region_view = 0;
1854 sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1856 /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1857 pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1859 dest_playlist->clear_changes ();
1861 dest_playlist->add_region (region, where, 1.0, false, sub_num);
1863 if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1864 dest_playlist->set_layer (region, dest_layer);
1869 assert (_new_region_view);
1871 return _new_region_view;
1875 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1877 _new_region_view = rv;
1881 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1883 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1884 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1886 _editor->session()->add_command (c);
1895 RegionMoveDrag::aborted (bool movement_occurred)
1899 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1900 list<DraggingView>::const_iterator next = i;
1909 RegionMotionDrag::aborted (movement_occurred);
1914 RegionMotionDrag::aborted (bool)
1916 for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1918 StreamView* sview = (*i)->view();
1921 if (sview->layer_display() == Expanded) {
1922 sview->set_layer_display (Stacked);
1927 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1928 RegionView* rv = i->view;
1929 TimeAxisView* tv = &(rv->get_time_axis_view ());
1930 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1932 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1933 rv->get_canvas_group()->set_y_position (0);
1935 rv->move (-_total_x_delta, 0);
1936 rv->set_height (rtv->view()->child_height ());
1940 /** @param b true to brush, otherwise false.
1941 * @param c true to make copies of the regions being moved, otherwise false.
1943 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1944 : RegionMotionDrag (e, i, p, v, b)
1946 , _new_region_view (0)
1948 DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1951 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1952 if (rtv && rtv->is_track()) {
1953 speed = rtv->track()->speed ();
1956 _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1960 RegionMoveDrag::setup_pointer_frame_offset ()
1962 _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1965 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1966 : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1968 DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1970 assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1971 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1973 _primary = v->view()->create_region_view (r, false, false);
1975 _primary->get_canvas_group()->show ();
1976 _primary->set_position (pos, 0);
1977 _views.push_back (DraggingView (_primary, this, v));
1979 _last_frame_position = pos;
1981 _item = _primary->get_canvas_group ();
1985 RegionInsertDrag::finished (GdkEvent * event, bool)
1987 int pos = _views.front().time_axis_view;
1988 assert(pos >= 0 && pos < (int)_time_axis_views.size());
1990 RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1992 _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1993 _primary->get_canvas_group()->set_y_position (0);
1995 boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1997 _editor->begin_reversible_command (Operations::insert_region);
1998 playlist->clear_changes ();
1999 playlist->add_region (_primary->region (), _last_frame_position);
2001 // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
2002 if (Config->get_edit_mode() == Ripple) {
2003 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
2006 _editor->session()->add_command (new StatefulDiffCommand (playlist));
2007 _editor->commit_reversible_command ();
2015 RegionInsertDrag::aborted (bool)
2022 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2023 : RegionMoveDrag (e, i, p, v, false, false)
2025 DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2028 struct RegionSelectionByPosition {
2029 bool operator() (RegionView*a, RegionView* b) {
2030 return a->region()->position () < b->region()->position();
2035 RegionSpliceDrag::motion (GdkEvent* event, bool)
2037 /* Which trackview is this ? */
2039 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2040 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2042 /* The region motion is only processed if the pointer is over
2046 if (!tv || !tv->is_track()) {
2047 /* To make sure we hide the verbose canvas cursor when the mouse is
2048 not held over an audio track.
2050 _editor->verbose_cursor()->hide ();
2053 _editor->verbose_cursor()->show ();
2058 if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2064 RegionSelection copy;
2065 _editor->selection->regions.by_position(copy);
2067 framepos_t const pf = adjusted_current_frame (event);
2069 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2071 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2077 boost::shared_ptr<Playlist> playlist;
2079 if ((playlist = atv->playlist()) == 0) {
2083 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2088 if (pf < (*i)->region()->last_frame() + 1) {
2092 if (pf > (*i)->region()->first_frame()) {
2098 playlist->shuffle ((*i)->region(), dir);
2103 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2105 RegionMoveDrag::finished (event, movement_occurred);
2109 RegionSpliceDrag::aborted (bool)
2119 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2122 boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2124 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2125 RegionSelection to_ripple;
2126 for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2127 if ((*i)->position() >= where) {
2128 to_ripple.push_back (rtv->view()->find_view(*i));
2132 for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2133 if (!exclude.contains (*i)) {
2134 // the selection has already been added to _views
2136 if (drag_in_progress) {
2137 // do the same things that RegionMotionDrag::motion does when
2138 // first_move is true, for the region views that we're adding
2139 // to _views this time
2142 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2143 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2144 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2145 rvg->reparent (_editor->_drag_motion_group);
2147 // we only need to move in the y direction
2148 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2153 _views.push_back (DraggingView (*i, this, tav));
2159 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2162 for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2163 // we added all the regions after the selection
2165 std::list<DraggingView>::iterator to_erase = i++;
2166 if (!_editor->selection->regions.contains (to_erase->view)) {
2167 // restore the non-selected regions to their original playlist & positions,
2168 // and then ripple them back by the length of the regions that were dragged away
2169 // do the same things as RegionMotionDrag::aborted
2171 RegionView *rv = to_erase->view;
2172 TimeAxisView* tv = &(rv->get_time_axis_view ());
2173 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2176 // plonk them back onto their own track
2177 rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2178 rv->get_canvas_group()->set_y_position (0);
2182 // move the underlying region to match the view
2183 rv->region()->set_position (rv->region()->position() + amount);
2185 // restore the view to match the underlying region's original position
2186 rv->move(-amount, 0); // second parameter is y delta - seems 0 is OK
2189 rv->set_height (rtv->view()->child_height ());
2190 _views.erase (to_erase);
2196 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2198 if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2200 return allow_moves_across_tracks;
2208 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2209 : RegionMoveDrag (e, i, p, v, false, false)
2211 DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2212 // compute length of selection
2213 RegionSelection selected_regions = _editor->selection->regions;
2214 selection_length = selected_regions.end_frame() - selected_regions.start();
2216 // we'll only allow dragging to another track in ripple mode if all the regions
2217 // being dragged start off on the same track
2218 allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2221 exclude = new RegionList;
2222 for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2223 exclude->push_back((*i)->region());
2226 // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2227 RegionSelection copy;
2228 selected_regions.by_position(copy); // get selected regions sorted by position into copy
2230 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2231 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2233 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2234 // find ripple start point on each applicable playlist
2235 RegionView *first_selected_on_this_track = NULL;
2236 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2237 if ((*i)->region()->playlist() == (*pi)) {
2238 // region is on this playlist - it's the first, because they're sorted
2239 first_selected_on_this_track = *i;
2243 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2244 add_all_after_to_views (
2245 &first_selected_on_this_track->get_time_axis_view(),
2246 first_selected_on_this_track->region()->position(),
2247 selected_regions, false);
2250 if (allow_moves_across_tracks) {
2251 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2259 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2261 /* Which trackview is this ? */
2263 pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2264 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2266 /* The region motion is only processed if the pointer is over
2270 if (!tv || !tv->is_track()) {
2271 /* To make sure we hide the verbose canvas cursor when the mouse is
2272 not held over an audiotrack.
2274 _editor->verbose_cursor()->hide ();
2278 framepos_t where = adjusted_current_frame (event);
2279 assert (where >= 0);
2281 double delta = compute_x_delta (event, &after);
2283 framecnt_t amount = _editor->pixel_to_sample (delta);
2285 if (allow_moves_across_tracks) {
2286 // all the originally selected regions were on the same track
2288 framecnt_t adjust = 0;
2289 if (prev_tav && tv != prev_tav) {
2290 // dragged onto a different track
2291 // remove the unselected regions from _views, restore them to their original positions
2292 // and add the regions after the drop point on the new playlist to _views instead.
2293 // undo the effect of rippling the previous playlist, and include the effect of removing
2294 // the dragged region(s) from this track
2296 remove_unselected_from_views (prev_amount, false);
2297 // ripple previous playlist according to the regions that have been removed onto the new playlist
2298 prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2301 // move just the selected regions
2302 RegionMoveDrag::motion(event, first_move);
2304 // ensure that the ripple operation on the new playlist inserts selection_length time
2305 adjust = selection_length;
2306 // ripple the new current playlist
2307 tv->playlist()->ripple (where, amount+adjust, exclude);
2309 // add regions after point where drag entered this track to subsequent ripples
2310 add_all_after_to_views (tv, where, _editor->selection->regions, true);
2313 // motion on same track
2314 RegionMoveDrag::motion(event, first_move);
2318 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2319 prev_position = where;
2321 // selection encompasses multiple tracks - just drag
2322 // cross-track drags are forbidden
2323 RegionMoveDrag::motion(event, first_move);
2326 if (!_x_constrained) {
2327 prev_amount += amount;
2330 _last_frame_position = after;
2334 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2336 if (!movement_occurred) {
2340 if (was_double_click() && !_views.empty()) {
2341 DraggingView dv = _views.front();
2342 _editor->edit_region (dv.view);
2348 _editor->begin_reversible_command(_("Ripple drag"));
2350 // remove the regions being rippled from the dragging view, updating them to
2351 // their new positions
2352 remove_unselected_from_views (prev_amount, true);
2354 if (allow_moves_across_tracks) {
2356 // if regions were dragged across tracks, we've rippled any later
2357 // regions on the track the regions were dragged off, so we need
2358 // to add the original track to the undo record
2359 orig_tav->playlist()->clear_changes();
2360 vector<Command*> cmds;
2361 orig_tav->playlist()->rdiff (cmds);
2362 _editor->session()->add_commands (cmds);
2364 if (prev_tav && prev_tav != orig_tav) {
2365 prev_tav->playlist()->clear_changes();
2366 vector<Command*> cmds;
2367 prev_tav->playlist()->rdiff (cmds);
2368 _editor->session()->add_commands (cmds);
2371 // selection spanned multiple tracks - all will need adding to undo record
2373 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2374 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2376 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2377 (*pi)->clear_changes();
2378 vector<Command*> cmds;
2379 (*pi)->rdiff (cmds);
2380 _editor->session()->add_commands (cmds);
2384 // other modified playlists are added to undo by RegionMoveDrag::finished()
2385 RegionMoveDrag::finished (event, movement_occurred);
2386 _editor->commit_reversible_command();
2390 RegionRippleDrag::aborted (bool movement_occurred)
2392 RegionMoveDrag::aborted (movement_occurred);
2397 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2399 _view (dynamic_cast<MidiTimeAxisView*> (v))
2401 DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2407 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2410 _editor->begin_reversible_command (_("create region"));
2411 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2412 _view->playlist()->freeze ();
2415 framepos_t const f = adjusted_current_frame (event);
2416 if (f < grab_frame()) {
2417 _region->set_initial_position (f);
2420 /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2421 so that if this region is duplicated, its duplicate starts on
2422 a snap point rather than 1 frame after a snap point. Otherwise things get
2423 a bit confusing as if a region starts 1 frame after a snap point, one cannot
2424 place snapped notes at the start of the region.
2427 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2428 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2434 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2436 if (!movement_occurred) {
2437 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2439 _view->playlist()->thaw ();
2440 _editor->commit_reversible_command();
2445 RegionCreateDrag::aborted (bool)
2448 _view->playlist()->thaw ();
2454 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2459 , _was_selected (false)
2462 DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2466 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2468 Gdk::Cursor* cursor;
2469 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2471 float x_fraction = cnote->mouse_x_fraction ();
2473 if (x_fraction > 0.0 && x_fraction < 0.25) {
2474 cursor = _editor->cursors()->left_side_trim;
2477 cursor = _editor->cursors()->right_side_trim;
2481 Drag::start_grab (event, cursor);
2483 region = &cnote->region_view();
2486 temp = region->snap_to_pixel (cnote->x0 (), true);
2487 _snap_delta = temp - cnote->x0 ();
2491 if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2496 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2497 if (ms.size() > 1) {
2498 /* has to be relative, may make no sense otherwise */
2502 if (!(_was_selected = cnote->selected())) {
2504 /* tertiary-click means extend selection - we'll do that on button release,
2505 so don't add it here, because otherwise we make it hard to figure
2506 out the "extend-to" range.
2509 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2512 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2515 region->note_selected (cnote, true);
2517 _editor->get_selection().clear_points();
2518 region->unique_select (cnote);
2525 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2527 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2529 _editor->begin_reversible_command (_("resize notes"));
2531 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2532 MidiRegionSelection::iterator next;
2535 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2537 mrv->begin_resizing (at_front);
2543 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2544 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2546 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2550 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2552 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2553 if (_editor->snap_mode () != SnapOff) {
2557 if (_editor->snap_mode () == SnapOff) {
2559 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2560 if (apply_snap_delta) {
2566 if (apply_snap_delta) {
2570 mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2576 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2578 if (!movement_occurred) {
2579 /* no motion - select note */
2580 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2581 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2582 _editor->current_mouse_mode() == Editing::MouseDraw) {
2584 bool changed = false;
2586 if (_was_selected) {
2587 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2589 region->note_deselected (cnote);
2592 _editor->get_selection().clear_points();
2593 region->unique_select (cnote);
2597 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2598 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2600 if (!extend && !add && region->selection_size() > 1) {
2601 _editor->get_selection().clear_points();
2602 region->unique_select (cnote);
2604 } else if (extend) {
2605 region->note_selected (cnote, true, true);
2608 /* it was added during button press */
2614 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2615 _editor->commit_reversible_selection_op();
2622 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2623 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2624 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2626 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2629 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2631 if (ArdourKeyboard::indicates_snap (event->button.state)) {
2632 if (_editor->snap_mode () != SnapOff) {
2636 if (_editor->snap_mode () == SnapOff) {
2638 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2639 if (apply_snap_delta) {
2645 if (apply_snap_delta) {
2649 mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2653 _editor->commit_reversible_command ();
2657 NoteResizeDrag::aborted (bool)
2659 MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2660 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2661 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2663 mrv->abort_resizing ();
2668 AVDraggingView::AVDraggingView (RegionView* v)
2671 initial_position = v->region()->position ();
2674 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2677 DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2680 TrackViewList empty;
2682 _editor->get_regions_after(rs, (framepos_t) 0, empty);
2683 std::list<RegionView*> views = rs.by_layer();
2686 for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2687 RegionView* rv = (*i);
2688 if (!rv->region()->video_locked()) {
2691 if (rv->region()->locked()) {
2694 _views.push_back (AVDraggingView (rv));
2699 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2701 Drag::start_grab (event);
2702 if (_editor->session() == 0) {
2706 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2712 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2716 _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2717 _max_backwards_drag = (
2718 ARDOUR_UI::instance()->video_timeline->get_duration()
2719 + ARDOUR_UI::instance()->video_timeline->get_offset()
2720 - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2723 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2724 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2725 _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2728 DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2731 Timecode::Time timecode;
2732 _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2733 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);
2734 show_verbose_cursor_text (buf);
2738 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2740 if (_editor->session() == 0) {
2743 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2747 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2751 framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2752 dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2754 if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2755 dt = - _max_backwards_drag;
2758 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2759 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2761 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2762 RegionView* rv = i->view;
2763 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2766 rv->region()->clear_changes ();
2767 rv->region()->suspend_property_changes();
2769 rv->region()->set_position(i->initial_position + dt);
2770 rv->region_changed(ARDOUR::Properties::position);
2773 const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2774 Timecode::Time timecode;
2775 Timecode::Time timediff;
2777 _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2778 _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2779 snprintf (buf, sizeof (buf),
2780 "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2781 "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2782 , _("Video Start:"),
2783 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2785 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2787 show_verbose_cursor_text (buf);
2791 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2793 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2800 if (!movement_occurred || ! _editor->session()) {
2804 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2806 _editor->begin_reversible_command (_("Move Video"));
2808 XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2809 ARDOUR_UI::instance()->video_timeline->save_undo();
2810 XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2811 _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2813 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2814 i->view->drag_end();
2815 i->view->region()->resume_property_changes ();
2817 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2820 _editor->session()->maybe_update_session_range(
2821 std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2822 std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2826 _editor->commit_reversible_command ();
2830 VideoTimeLineDrag::aborted (bool)
2832 if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2835 ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2836 ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2838 for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2839 i->view->region()->resume_property_changes ();
2840 i->view->region()->set_position(i->initial_position);
2844 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2845 : RegionDrag (e, i, p, v)
2846 , _operation (StartTrim)
2847 , _preserve_fade_anchor (preserve_fade_anchor)
2848 , _jump_position_when_done (false)
2850 DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2854 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2857 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2858 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2860 if (tv && tv->is_track()) {
2861 speed = tv->track()->speed();
2864 framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2865 framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2866 framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2868 framepos_t const pf = adjusted_current_frame (event);
2869 setup_snap_delta (region_start);
2871 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2872 /* Move the contents of the region around without changing the region bounds */
2873 _operation = ContentsTrim;
2874 Drag::start_grab (event, _editor->cursors()->trimmer);
2876 /* These will get overridden for a point trim.*/
2877 if (pf < (region_start + region_length/2)) {
2878 /* closer to front */
2879 _operation = StartTrim;
2880 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2881 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2883 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2887 _operation = EndTrim;
2888 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2889 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2891 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2895 /* jump trim disabled for now
2896 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2897 _jump_position_when_done = true;
2901 switch (_operation) {
2903 show_verbose_cursor_time (region_start);
2906 show_verbose_cursor_duration (region_start, region_end);
2909 show_verbose_cursor_time (pf);
2913 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2914 i->view->region()->suspend_property_changes ();
2919 TrimDrag::motion (GdkEvent* event, bool first_move)
2921 RegionView* rv = _primary;
2924 TimeAxisView* tvp = &_primary->get_time_axis_view ();
2925 RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2926 pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2927 frameoffset_t frame_delta = 0;
2929 if (tv && tv->is_track()) {
2930 speed = tv->track()->speed();
2932 framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2933 framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2939 switch (_operation) {
2941 trim_type = "Region start trim";
2944 trim_type = "Region end trim";
2947 trim_type = "Region content trim";
2954 _editor->begin_reversible_command (trim_type);
2956 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2957 RegionView* rv = i->view;
2958 rv->region()->playlist()->clear_owned_changes ();
2960 if (_operation == StartTrim) {
2961 rv->trim_front_starting ();
2964 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2967 arv->temporarily_hide_envelope ();
2971 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2972 insert_result = _editor->motion_frozen_playlists.insert (pl);
2974 if (insert_result.second) {
2980 bool non_overlap_trim = false;
2982 if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2983 non_overlap_trim = true;
2986 /* contstrain trim to fade length */
2987 if (_preserve_fade_anchor) {
2988 switch (_operation) {
2990 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2991 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2993 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2994 if (ar->locked()) continue;
2995 framecnt_t len = ar->fade_in()->back()->when;
2996 if (len < dt) dt = min(dt, len);
3000 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3001 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3003 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3004 if (ar->locked()) continue;
3005 framecnt_t len = ar->fade_out()->back()->when;
3006 if (len < -dt) dt = max(dt, -len);
3014 int32_t divisions = current_music_divisor (adj_frame, event->button.state);
3016 switch (_operation) {
3018 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3019 bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
3022 if (changed && _preserve_fade_anchor) {
3023 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3025 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3026 framecnt_t len = ar->fade_in()->back()->when;
3027 framecnt_t diff = ar->first_frame() - i->initial_position;
3028 framepos_t new_length = len - diff;
3029 i->anchored_fade_length = min (ar->length(), new_length);
3030 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
3031 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3038 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3039 bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, divisions);
3040 if (changed && _preserve_fade_anchor) {
3041 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3043 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3044 framecnt_t len = ar->fade_out()->back()->when;
3045 framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3046 framepos_t new_length = len + diff;
3047 i->anchored_fade_length = min (ar->length(), new_length);
3048 //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
3049 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3057 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3059 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3060 i->view->move_contents (frame_delta);
3066 switch (_operation) {
3068 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3071 show_verbose_cursor_duration ((framepos_t) rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3074 // show_verbose_cursor_time (frame_delta);
3080 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3082 if (movement_occurred) {
3083 motion (event, false);
3085 if (_operation == StartTrim) {
3086 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3088 /* This must happen before the region's StatefulDiffCommand is created, as it may
3089 `correct' (ahem) the region's _start from being negative to being zero. It
3090 needs to be zero in the undo record.
3092 i->view->trim_front_ending ();
3094 if (_preserve_fade_anchor) {
3095 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3097 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3098 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3099 ar->set_fade_in_length(i->anchored_fade_length);
3100 ar->set_fade_in_active(true);
3103 if (_jump_position_when_done) {
3104 i->view->region()->set_position (i->initial_position);
3107 } else if (_operation == EndTrim) {
3108 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3109 if (_preserve_fade_anchor) {
3110 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3112 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3113 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3114 ar->set_fade_out_length(i->anchored_fade_length);
3115 ar->set_fade_out_active(true);
3118 if (_jump_position_when_done) {
3119 i->view->region()->set_position (i->initial_end - i->view->region()->length());
3124 if (!_views.empty()) {
3125 if (_operation == StartTrim) {
3126 _editor->maybe_locate_with_edit_preroll(
3127 _views.begin()->view->region()->position());
3129 if (_operation == EndTrim) {
3130 _editor->maybe_locate_with_edit_preroll(
3131 _views.begin()->view->region()->position() +
3132 _views.begin()->view->region()->length());
3136 if (!_editor->selection->selected (_primary)) {
3137 _primary->thaw_after_trim ();
3140 set<boost::shared_ptr<Playlist> > diffed_playlists;
3142 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3143 i->view->thaw_after_trim ();
3144 i->view->enable_display (true);
3146 /* Trimming one region may affect others on the playlist, so we need
3147 to get undo Commands from the whole playlist rather than just the
3148 region. Use diffed_playlists to make sure we don't diff a given
3149 playlist more than once.
3151 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3152 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3153 vector<Command*> cmds;
3155 _editor->session()->add_commands (cmds);
3156 diffed_playlists.insert (p);
3161 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3165 _editor->motion_frozen_playlists.clear ();
3166 _editor->commit_reversible_command();
3169 /* no mouse movement */
3170 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3171 _editor->point_trim (event, adjusted_current_frame (event));
3175 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3176 i->view->region()->resume_property_changes ();
3181 TrimDrag::aborted (bool movement_occurred)
3183 /* Our motion method is changing model state, so use the Undo system
3184 to cancel. Perhaps not ideal, as this will leave an Undo point
3185 behind which may be slightly odd from the user's point of view.
3189 finished (&ev, true);
3191 if (movement_occurred) {
3192 _editor->session()->undo (1);
3195 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3196 i->view->region()->resume_property_changes ();
3201 TrimDrag::setup_pointer_frame_offset ()
3203 list<DraggingView>::iterator i = _views.begin ();
3204 while (i != _views.end() && i->view != _primary) {
3208 if (i == _views.end()) {
3212 switch (_operation) {
3214 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3217 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3224 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3227 , _old_snap_type (e->snap_type())
3228 , _old_snap_mode (e->snap_mode())
3231 DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3232 _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3234 _real_section = &_marker->meter();
3239 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3241 Drag::start_grab (event, cursor);
3242 show_verbose_cursor_time (adjusted_current_frame(event));
3246 MeterMarkerDrag::setup_pointer_frame_offset ()
3248 _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3252 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3255 // create a dummy marker to catch events, then hide it.
3258 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3260 _marker = new MeterMarker (
3262 *_editor->meter_group,
3263 UIConfiguration::instance().color ("meter marker"),
3265 *new MeterSection (_marker->meter())
3268 /* use the new marker for the grab */
3269 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3272 TempoMap& map (_editor->session()->tempo_map());
3273 /* get current state */
3274 before_state = &map.get_state();
3277 _editor->begin_reversible_command (_("move meter mark"));
3279 _editor->begin_reversible_command (_("copy meter mark"));
3281 Timecode::BBT_Time bbt = _real_section->bbt();
3283 /* we can't add a meter where one currently exists */
3284 if (_real_section->frame() < adjusted_current_frame (event, false)) {
3289 const double beat = map.beat_at_bbt (bbt);
3290 const framepos_t frame = map.frame_at_beat (beat);
3291 _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3292 , beat, bbt, frame, _real_section->position_lock_style());
3293 if (!_real_section) {
3299 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3300 if (_real_section->position_lock_style() != AudioTime) {
3301 _editor->set_snap_to (SnapToBar);
3302 _editor->set_snap_mode (SnapNormal);
3306 framepos_t pf = adjusted_current_frame (event);
3308 if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3309 /* never snap to music for audio locked */
3310 pf = adjusted_current_frame (event, false);
3313 _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3315 /* fake marker meeds to stay under the mouse, unlike the real one. */
3316 _marker->set_position (adjusted_current_frame (event, false));
3318 show_verbose_cursor_time (_real_section->frame());
3322 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3324 if (!movement_occurred) {
3325 if (was_double_click()) {
3326 _editor->edit_meter_marker (*_marker);
3331 /* reinstate old snap setting */
3332 _editor->set_snap_to (_old_snap_type);
3333 _editor->set_snap_mode (_old_snap_mode);
3335 TempoMap& map (_editor->session()->tempo_map());
3337 XMLNode &after = map.get_state();
3338 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3339 _editor->commit_reversible_command ();
3341 // delete the dummy marker we used for visual representation while moving.
3342 // a new visual marker will show up automatically.
3347 MeterMarkerDrag::aborted (bool moved)
3349 _marker->set_position (_marker->meter().frame ());
3351 /* reinstate old snap setting */
3352 _editor->set_snap_to (_old_snap_type);
3353 _editor->set_snap_mode (_old_snap_mode);
3355 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3356 // delete the dummy marker we used for visual representation while moving.
3357 // a new visual marker will show up automatically.
3362 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3368 DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3370 _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3371 _real_section = &_marker->tempo();
3372 _movable = !_real_section->initial();
3373 _grab_bpm = _real_section->note_types_per_minute();
3378 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3380 Drag::start_grab (event, cursor);
3381 if (!_real_section->active()) {
3382 show_verbose_cursor_text (_("inactive"));
3384 show_verbose_cursor_time (adjusted_current_frame (event));
3389 TempoMarkerDrag::setup_pointer_frame_offset ()
3391 _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3395 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3397 if (!_real_section->active()) {
3403 // mvc drag - create a dummy marker to catch events, hide it.
3406 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3408 TempoSection section (_marker->tempo());
3410 _marker = new TempoMarker (
3412 *_editor->tempo_group,
3413 UIConfiguration::instance().color ("tempo marker"),
3415 *new TempoSection (_marker->tempo())
3418 /* use the new marker for the grab */
3419 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3422 TempoMap& map (_editor->session()->tempo_map());
3423 /* get current state */
3424 before_state = &map.get_state();
3427 _editor->begin_reversible_command (_("move tempo mark"));
3430 const Tempo tempo (_marker->tempo());
3431 const framepos_t frame = adjusted_current_frame (event) + 1;
3432 const TempoSection::Type type = _real_section->type();
3434 _editor->begin_reversible_command (_("copy tempo mark"));
3436 if (_real_section->position_lock_style() == MusicTime) {
3437 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3438 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3440 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3443 if (!_real_section) {
3451 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3452 /* use vertical movement to alter tempo .. should be log */
3453 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3455 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3457 show_verbose_cursor_text (strs.str());
3459 } else if (_movable && !_real_section->locked_to_meter()) {
3462 if (_editor->snap_musical()) {
3463 /* we can't snap to a grid that we are about to move.
3464 * gui_move_tempo() will sort out snap using the supplied beat divisions.
3466 pf = adjusted_current_frame (event, false);
3468 pf = adjusted_current_frame (event);
3471 TempoMap& map (_editor->session()->tempo_map());
3473 /* snap to beat is 1, snap to bar is -1 (sorry) */
3474 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3476 map.gui_set_tempo_position (_real_section, pf, sub_num);
3478 show_verbose_cursor_time (_real_section->frame());
3480 _marker->set_position (adjusted_current_frame (event, false));
3484 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3486 if (!_real_section->active()) {
3489 if (!movement_occurred) {
3490 if (was_double_click()) {
3491 _editor->edit_tempo_marker (*_marker);
3496 TempoMap& map (_editor->session()->tempo_map());
3498 XMLNode &after = map.get_state();
3499 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3500 _editor->commit_reversible_command ();
3502 // delete the dummy marker we used for visual representation while moving.
3503 // a new visual marker will show up automatically.
3508 TempoMarkerDrag::aborted (bool moved)
3510 _marker->set_position (_marker->tempo().frame());
3512 TempoMap& map (_editor->session()->tempo_map());
3513 map.set_state (*before_state, Stateful::current_state_version);
3514 // delete the dummy (hidden) marker we used for events while moving.
3519 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3525 DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3530 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3532 Drag::start_grab (event, cursor);
3533 TempoMap& map (_editor->session()->tempo_map());
3534 _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3537 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3538 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3539 show_verbose_cursor_text (sstr.str());
3540 finished (event, false);
3544 BBTRulerDrag::setup_pointer_frame_offset ()
3546 TempoMap& map (_editor->session()->tempo_map());
3547 const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3548 const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3551 if (divisions > 0) {
3552 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3554 /* while it makes some sense for the user to determine the division to 'grab',
3555 grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3556 and the result over steep tempo curves. Use sixteenths.
3558 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3561 _grab_qn = map.quarter_note_at_beat (beat);
3563 _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3568 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3570 TempoMap& map (_editor->session()->tempo_map());
3573 /* get current state */
3574 before_state = &map.get_state();
3575 _editor->begin_reversible_command (_("stretch tempo"));
3580 if (_editor->snap_musical()) {
3581 pf = adjusted_current_frame (event, false);
3583 pf = adjusted_current_frame (event);
3586 if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3587 /* adjust previous tempo to match pointer frame */
3588 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3591 sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3592 sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3593 show_verbose_cursor_text (sstr.str());
3597 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3599 if (!movement_occurred) {
3603 TempoMap& map (_editor->session()->tempo_map());
3605 XMLNode &after = map.get_state();
3606 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3607 _editor->commit_reversible_command ();
3611 BBTRulerDrag::aborted (bool moved)
3614 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3619 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3620 : Drag (e, &c.track_canvas_item(), false)
3625 DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3628 /** Do all the things we do when dragging the playhead to make it look as though
3629 * we have located, without actually doing the locate (because that would cause
3630 * the diskstream buffers to be refilled, which is too slow).
3633 CursorDrag::fake_locate (framepos_t t)
3635 if (_editor->session () == 0) {
3639 _editor->playhead_cursor->set_position (t);
3641 Session* s = _editor->session ();
3642 if (s->timecode_transmission_suspended ()) {
3643 framepos_t const f = _editor->playhead_cursor->current_frame ();
3644 /* This is asynchronous so it will be sent "now"
3646 s->send_mmc_locate (f);
3647 /* These are synchronous and will be sent during the next
3650 s->queue_full_time_code ();
3651 s->queue_song_position_pointer ();
3654 show_verbose_cursor_time (t);
3655 _editor->UpdateAllTransportClocks (t);
3659 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3661 Drag::start_grab (event, c);
3662 setup_snap_delta (_editor->playhead_cursor->current_frame ());
3664 _grab_zoom = _editor->samples_per_pixel;
3666 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3668 _editor->snap_to_with_modifier (where, event);
3670 _editor->_dragging_playhead = true;
3672 Session* s = _editor->session ();
3674 /* grab the track canvas item as well */
3676 _cursor.track_canvas_item().grab();
3679 if (_was_rolling && _stop) {
3683 if (s->is_auditioning()) {
3684 s->cancel_audition ();
3688 if (AudioEngine::instance()->connected()) {
3690 /* do this only if we're the engine is connected
3691 * because otherwise this request will never be
3692 * serviced and we'll busy wait forever. likewise,
3693 * notice if we are disconnected while waiting for the
3694 * request to be serviced.
3697 s->request_suspend_timecode_transmission ();
3698 while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3699 /* twiddle our thumbs */
3704 fake_locate (where - snap_delta (event->button.state));
3708 CursorDrag::motion (GdkEvent* event, bool)
3710 framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3711 _editor->snap_to_with_modifier (where, event);
3712 if (where != last_pointer_frame()) {
3713 fake_locate (where - snap_delta (event->button.state));
3718 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3720 _editor->_dragging_playhead = false;
3722 _cursor.track_canvas_item().ungrab();
3724 if (!movement_occurred && _stop) {
3728 motion (event, false);
3730 Session* s = _editor->session ();
3732 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3733 _editor->_pending_locate_request = true;
3734 s->request_resume_timecode_transmission ();
3739 CursorDrag::aborted (bool)
3741 _cursor.track_canvas_item().ungrab();
3743 if (_editor->_dragging_playhead) {
3744 _editor->session()->request_resume_timecode_transmission ();
3745 _editor->_dragging_playhead = false;
3748 _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3751 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3752 : RegionDrag (e, i, p, v)
3754 DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3758 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3760 Drag::start_grab (event, cursor);
3762 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3763 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3764 setup_snap_delta (r->position ());
3766 show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3770 FadeInDrag::setup_pointer_frame_offset ()
3772 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3773 boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3774 _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3778 FadeInDrag::motion (GdkEvent* event, bool)
3780 framecnt_t fade_length;
3782 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3783 _editor->snap_to_with_modifier (pos, event);
3784 pos -= snap_delta (event->button.state);
3786 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3788 if (pos < (region->position() + 64)) {
3789 fade_length = 64; // this should be a minimum defined somewhere
3790 } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3791 fade_length = region->length() - region->fade_out()->back()->when - 1;
3793 fade_length = pos - region->position();
3796 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(), fade_length);
3807 show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3811 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3813 if (!movement_occurred) {
3817 framecnt_t fade_length;
3818 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3819 _editor->snap_to_with_modifier (pos, event);
3820 pos -= snap_delta (event->button.state);
3822 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3824 if (pos < (region->position() + 64)) {
3825 fade_length = 64; // this should be a minimum defined somewhere
3826 } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3827 fade_length = region->length() - region->fade_out()->back()->when - 1;
3829 fade_length = pos - region->position();
3832 bool in_command = false;
3834 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3836 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3842 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3843 XMLNode &before = alist->get_state();
3845 tmp->audio_region()->set_fade_in_length (fade_length);
3846 tmp->audio_region()->set_fade_in_active (true);
3849 _editor->begin_reversible_command (_("change fade in length"));
3852 XMLNode &after = alist->get_state();
3853 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3857 _editor->commit_reversible_command ();
3862 FadeInDrag::aborted (bool)
3864 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3865 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3871 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3875 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3876 : RegionDrag (e, i, p, v)
3878 DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3882 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3884 Drag::start_grab (event, cursor);
3886 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3887 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3888 setup_snap_delta (r->last_frame ());
3890 show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3894 FadeOutDrag::setup_pointer_frame_offset ()
3896 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3897 boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3898 _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3902 FadeOutDrag::motion (GdkEvent* event, bool)
3904 framecnt_t fade_length;
3906 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3907 _editor->snap_to_with_modifier (pos, event);
3908 pos -= snap_delta (event->button.state);
3910 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3912 if (pos > (region->last_frame() - 64)) {
3913 fade_length = 64; // this should really be a minimum fade defined somewhere
3914 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3915 fade_length = region->length() - region->fade_in()->back()->when - 1;
3917 fade_length = region->last_frame() - pos;
3920 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3922 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3928 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3931 show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3935 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3937 if (!movement_occurred) {
3941 framecnt_t fade_length;
3943 framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3944 _editor->snap_to_with_modifier (pos, event);
3945 pos -= snap_delta (event->button.state);
3947 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3949 if (pos > (region->last_frame() - 64)) {
3950 fade_length = 64; // this should really be a minimum fade defined somewhere
3951 } else if (pos <= region->position() + region->fade_in()->back()->when) {
3952 fade_length = region->length() - region->fade_in()->back()->when - 1;
3954 fade_length = region->last_frame() - pos;
3957 bool in_command = false;
3959 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3961 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3967 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3968 XMLNode &before = alist->get_state();
3970 tmp->audio_region()->set_fade_out_length (fade_length);
3971 tmp->audio_region()->set_fade_out_active (true);
3974 _editor->begin_reversible_command (_("change fade out length"));
3977 XMLNode &after = alist->get_state();
3978 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3982 _editor->commit_reversible_command ();
3987 FadeOutDrag::aborted (bool)
3989 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3990 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3996 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4000 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4002 , _selection_changed (false)
4004 DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4005 Gtk::Window* toplevel = _editor->current_toplevel();
4006 _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4010 _points.push_back (ArdourCanvas::Duple (0, 0));
4012 _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4015 MarkerDrag::~MarkerDrag ()
4017 for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4022 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4024 location = new Location (*l);
4025 markers.push_back (m);
4030 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4032 Drag::start_grab (event, cursor);
4036 Location *location = _editor->find_location_from_marker (_marker, is_start);
4037 _editor->_dragging_edit_point = true;
4039 update_item (location);
4041 // _drag_line->show();
4042 // _line->raise_to_top();
4045 show_verbose_cursor_time (location->start());
4047 show_verbose_cursor_time (location->end());
4049 setup_snap_delta (is_start ? location->start() : location->end());
4051 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4054 case Selection::Toggle:
4055 /* we toggle on the button release */
4057 case Selection::Set:
4058 if (!_editor->selection->selected (_marker)) {
4059 _editor->selection->set (_marker);
4060 _selection_changed = true;
4063 case Selection::Extend:
4065 Locations::LocationList ll;
4066 list<ArdourMarker*> to_add;
4068 _editor->selection->markers.range (s, e);
4069 s = min (_marker->position(), s);
4070 e = max (_marker->position(), e);
4073 if (e < max_framepos) {
4076 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4077 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4078 Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4081 to_add.push_back (lm->start);
4084 to_add.push_back (lm->end);
4088 if (!to_add.empty()) {
4089 _editor->selection->add (to_add);
4090 _selection_changed = true;
4094 case Selection::Add:
4095 _editor->selection->add (_marker);
4096 _selection_changed = true;
4101 /* Set up copies for us to manipulate during the drag
4104 for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4106 Location* l = _editor->find_location_from_marker (*i, is_start);
4113 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4115 /* range: check that the other end of the range isn't
4118 CopiedLocationInfo::iterator x;
4119 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4120 if (*(*x).location == *l) {
4124 if (x == _copied_locations.end()) {
4125 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4127 (*x).markers.push_back (*i);
4128 (*x).move_both = true;
4136 MarkerDrag::setup_pointer_frame_offset ()
4139 Location *location = _editor->find_location_from_marker (_marker, is_start);
4140 _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4144 MarkerDrag::motion (GdkEvent* event, bool)
4146 framecnt_t f_delta = 0;
4148 bool move_both = false;
4149 Location *real_location;
4150 Location *copy_location = 0;
4151 framecnt_t const sd = snap_delta (event->button.state);
4153 framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4154 framepos_t next = newframe;
4156 if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4160 CopiedLocationInfo::iterator x;
4162 /* find the marker we're dragging, and compute the delta */
4164 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4166 copy_location = (*x).location;
4168 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4170 /* this marker is represented by this
4171 * CopiedLocationMarkerInfo
4174 if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4179 if (real_location->is_mark()) {
4180 f_delta = newframe - copy_location->start();
4184 switch (_marker->type()) {
4185 case ArdourMarker::SessionStart:
4186 case ArdourMarker::RangeStart:
4187 case ArdourMarker::LoopStart:
4188 case ArdourMarker::PunchIn:
4189 f_delta = newframe - copy_location->start();
4192 case ArdourMarker::SessionEnd:
4193 case ArdourMarker::RangeEnd:
4194 case ArdourMarker::LoopEnd:
4195 case ArdourMarker::PunchOut:
4196 f_delta = newframe - copy_location->end();
4199 /* what kind of marker is this ? */
4208 if (x == _copied_locations.end()) {
4209 /* hmm, impossible - we didn't find the dragged marker */
4213 /* now move them all */
4215 for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4217 copy_location = x->location;
4219 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4223 if (real_location->locked()) {
4227 if (copy_location->is_mark()) {
4231 copy_location->set_start (copy_location->start() + f_delta);
4235 framepos_t new_start = copy_location->start() + f_delta;
4236 framepos_t new_end = copy_location->end() + f_delta;
4238 if (is_start) { // start-of-range marker
4240 if (move_both || (*x).move_both) {
4241 copy_location->set_start (new_start);
4242 copy_location->set_end (new_end);
4243 } else if (new_start < copy_location->end()) {
4244 copy_location->set_start (new_start);
4245 } else if (newframe > 0) {
4246 //_editor->snap_to (next, RoundUpAlways, true);
4247 copy_location->set_end (next);
4248 copy_location->set_start (newframe);
4251 } else { // end marker
4253 if (move_both || (*x).move_both) {
4254 copy_location->set_end (new_end);
4255 copy_location->set_start (new_start);
4256 } else if (new_end > copy_location->start()) {
4257 copy_location->set_end (new_end);
4258 } else if (newframe > 0) {
4259 //_editor->snap_to (next, RoundDownAlways, true);
4260 copy_location->set_start (next);
4261 copy_location->set_end (newframe);
4266 update_item (copy_location);
4268 /* now lookup the actual GUI items used to display this
4269 * location and move them to wherever the copy of the location
4270 * is now. This means that the logic in ARDOUR::Location is
4271 * still enforced, even though we are not (yet) modifying
4272 * the real Location itself.
4275 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4278 lm->set_position (copy_location->start(), copy_location->end());
4283 assert (!_copied_locations.empty());
4285 show_verbose_cursor_time (newframe);
4289 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4291 if (!movement_occurred) {
4293 if (was_double_click()) {
4294 _editor->rename_marker (_marker);
4298 /* just a click, do nothing but finish
4299 off the selection process
4302 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4304 case Selection::Set:
4305 if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4306 _editor->selection->set (_marker);
4307 _selection_changed = true;
4311 case Selection::Toggle:
4312 /* we toggle on the button release, click only */
4313 _editor->selection->toggle (_marker);
4314 _selection_changed = true;
4318 case Selection::Extend:
4319 case Selection::Add:
4323 if (_selection_changed) {
4324 _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4325 _editor->commit_reversible_selection_op();
4331 _editor->_dragging_edit_point = false;
4333 XMLNode &before = _editor->session()->locations()->get_state();
4334 bool in_command = false;
4336 MarkerSelection::iterator i;
4337 CopiedLocationInfo::iterator x;
4340 for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4341 x != _copied_locations.end() && i != _editor->selection->markers.end();
4344 Location * location = _editor->find_location_from_marker (*i, is_start);
4348 if (location->locked()) {
4352 _editor->begin_reversible_command ( _("move marker") );
4355 if (location->is_mark()) {
4356 location->set_start (((*x).location)->start());
4358 location->set (((*x).location)->start(), ((*x).location)->end());
4361 if (location->is_session_range()) {
4362 _editor->session()->set_end_is_free (false);
4368 XMLNode &after = _editor->session()->locations()->get_state();
4369 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4370 _editor->commit_reversible_command ();
4375 MarkerDrag::aborted (bool movement_occurred)
4377 if (!movement_occurred) {
4381 for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4383 /* move all markers to their original location */
4386 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4389 Location * location = _editor->find_location_from_marker (*m, is_start);
4392 (*m)->set_position (is_start ? location->start() : location->end());
4399 MarkerDrag::update_item (Location*)
4404 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4406 , _fixed_grab_x (0.0)
4407 , _fixed_grab_y (0.0)
4408 , _cumulative_x_drag (0.0)
4409 , _cumulative_y_drag (0.0)
4413 if (_zero_gain_fraction < 0.0) {
4414 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4417 DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4419 _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4425 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4427 Drag::start_grab (event, _editor->cursors()->fader);
4429 // start the grab at the center of the control point so
4430 // the point doesn't 'jump' to the mouse after the first drag
4431 _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4432 _fixed_grab_y = _point->get_y();
4434 framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4435 setup_snap_delta (pos);
4437 float const fraction = 1 - (_point->get_y() / _point->line().height());
4438 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4440 _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4442 if (!_point->can_slide ()) {
4443 _x_constrained = true;
4448 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4450 double dx = _drags->current_pointer_x() - last_pointer_x();
4451 double dy = current_pointer_y() - last_pointer_y();
4452 bool need_snap = true;
4454 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4460 /* coordinate in pixels relative to the start of the region (for region-based automation)
4461 or track (for track-based automation) */
4462 double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4463 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4465 // calculate zero crossing point. back off by .01 to stay on the
4466 // positive side of zero
4467 double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4469 if (_x_constrained) {
4472 if (_y_constrained) {
4476 _cumulative_x_drag = cx - _fixed_grab_x;
4477 _cumulative_y_drag = cy - _fixed_grab_y;
4481 cy = min ((double) _point->line().height(), cy);
4483 // make sure we hit zero when passing through
4484 if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4488 framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4489 if (!_x_constrained && need_snap) {
4490 _editor->snap_to_with_modifier (cx_frames, event);
4493 cx_frames -= snap_delta (event->button.state);
4494 cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4496 float const fraction = 1.0 - (cy / _point->line().height());
4499 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4500 _editor->begin_reversible_command (_("automation event move"));
4501 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4503 pair<double, float> result;
4504 result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4506 show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4510 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4512 if (!movement_occurred) {
4515 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4516 _editor->reset_point_selection ();
4520 _point->line().end_drag (_pushing, _final_index);
4521 _editor->commit_reversible_command ();
4526 ControlPointDrag::aborted (bool)
4528 _point->line().reset ();
4532 ControlPointDrag::active (Editing::MouseMode m)
4534 if (m == Editing::MouseDraw) {
4535 /* always active in mouse draw */
4539 /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4540 return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4543 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4546 , _fixed_grab_x (0.0)
4547 , _fixed_grab_y (0.0)
4548 , _cumulative_y_drag (0)
4552 DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4556 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4558 _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4561 _item = &_line->grab_item ();
4563 /* need to get x coordinate in terms of parent (TimeAxisItemView)
4564 origin, and ditto for y.
4567 double mx = event->button.x;
4568 double my = event->button.y;
4570 _line->grab_item().canvas_to_item (mx, my);
4572 framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4574 if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4575 /* no adjacent points */
4579 Drag::start_grab (event, _editor->cursors()->fader);
4581 /* store grab start in item frame */
4582 double const bx = _line->nth (_before)->get_x();
4583 double const ax = _line->nth (_after)->get_x();
4584 double const click_ratio = (ax - mx) / (ax - bx);
4586 double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4591 double fraction = 1.0 - (cy / _line->height());
4593 show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4597 LineDrag::motion (GdkEvent* event, bool first_move)
4599 double dy = current_pointer_y() - last_pointer_y();
4601 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4605 double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4607 _cumulative_y_drag = cy - _fixed_grab_y;
4610 cy = min ((double) _line->height(), cy);
4612 double const fraction = 1.0 - (cy / _line->height());
4616 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4618 _editor->begin_reversible_command (_("automation range move"));
4619 _line->start_drag_line (_before, _after, initial_fraction);
4622 /* we are ignoring x position for this drag, so we can just pass in anything */
4623 pair<double, float> result;
4625 result = _line->drag_motion (0, fraction, true, false, ignored);
4626 show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4630 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4632 if (movement_occurred) {
4633 motion (event, false);
4634 _line->end_drag (false, 0);
4635 _editor->commit_reversible_command ();
4637 /* add a new control point on the line */
4639 AutomationTimeAxisView* atv;
4641 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4642 framepos_t where = grab_frame ();
4645 double cy = _fixed_grab_y;
4647 _line->grab_item().item_to_canvas (cx, cy);
4649 atv->add_automation_event (event, where, cy, false);
4650 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4651 AudioRegionView* arv;
4653 if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4654 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4661 LineDrag::aborted (bool)
4666 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4670 _region_view_grab_x (0.0),
4671 _cumulative_x_drag (0),
4675 DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4679 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4681 Drag::start_grab (event);
4683 _line = reinterpret_cast<Line*> (_item);
4686 /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4688 double cx = event->button.x;
4689 double cy = event->button.y;
4691 _item->parent()->canvas_to_item (cx, cy);
4693 /* store grab start in parent frame */
4694 _region_view_grab_x = cx;
4696 _before = *(float*) _item->get_data ("position");
4698 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4700 _max_x = _editor->sample_to_pixel(_arv->get_duration());
4704 FeatureLineDrag::motion (GdkEvent*, bool)
4706 double dx = _drags->current_pointer_x() - last_pointer_x();
4708 double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4710 _cumulative_x_drag += dx;
4712 /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4721 boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4723 _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4725 float *pos = new float;
4728 _line->set_data ("position", pos);
4734 FeatureLineDrag::finished (GdkEvent*, bool)
4736 _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4737 _arv->update_transient(_before, _before);
4741 FeatureLineDrag::aborted (bool)
4746 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4748 , _vertical_only (false)
4750 DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4754 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4756 Drag::start_grab (event);
4757 show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4761 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4768 framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4770 framepos_t grab = grab_frame ();
4771 if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4772 _editor->snap_to_with_modifier (grab, event);
4774 grab = raw_grab_frame ();
4777 /* base start and end on initial click position */
4787 if (current_pointer_y() < grab_y()) {
4788 y1 = current_pointer_y();
4791 y2 = current_pointer_y();
4795 if (start != end || y1 != y2) {
4797 double x1 = _editor->sample_to_pixel (start);
4798 double x2 = _editor->sample_to_pixel (end);
4799 const double min_dimension = 2.0;
4801 if (_vertical_only) {
4802 /* fixed 10 pixel width */
4806 x2 = min (x1 - min_dimension, x2);
4808 x2 = max (x1 + min_dimension, x2);
4813 y2 = min (y1 - min_dimension, y2);
4815 y2 = max (y1 + min_dimension, y2);
4818 /* translate rect into item space and set */
4820 ArdourCanvas::Rect r (x1, y1, x2, y2);
4822 /* this drag is a _trackview_only == true drag, so the y1 and
4823 * y2 (computed using current_pointer_y() and grab_y()) will be
4824 * relative to the top of the trackview group). The
4825 * rubberband rect has the same parent/scroll offset as the
4826 * the trackview group, so we can use the "r" rect directly
4827 * to set the shape of the rubberband.
4830 _editor->rubberband_rect->set (r);
4831 _editor->rubberband_rect->show();
4832 _editor->rubberband_rect->raise_to_top();
4834 show_verbose_cursor_time (pf);
4836 do_select_things (event, true);
4841 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4845 framepos_t grab = grab_frame ();
4846 framepos_t lpf = last_pointer_frame ();
4848 if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4849 grab = raw_grab_frame ();
4850 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4864 if (current_pointer_y() < grab_y()) {
4865 y1 = current_pointer_y();
4868 y2 = current_pointer_y();
4872 select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4876 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4878 if (movement_occurred) {
4880 motion (event, false);
4881 do_select_things (event, false);
4887 bool do_deselect = true;
4888 MidiTimeAxisView* mtv;
4890 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4892 if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4893 /* nothing selected */
4894 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4895 do_deselect = false;
4899 /* do not deselect if Primary or Tertiary (toggle-select or
4900 * extend-select are pressed.
4903 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4904 !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4911 _editor->rubberband_rect->hide();
4915 RubberbandSelectDrag::aborted (bool)
4917 _editor->rubberband_rect->hide ();
4920 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4921 : RegionDrag (e, i, p, v)
4923 DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4927 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4929 Drag::start_grab (event, cursor);
4931 _editor->get_selection().add (_primary);
4933 framepos_t where = _primary->region()->position();
4934 setup_snap_delta (where);
4936 show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4940 TimeFXDrag::motion (GdkEvent* event, bool)
4942 RegionView* rv = _primary;
4943 StreamView* cv = rv->get_time_axis_view().view ();
4945 pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4946 int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4947 int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4948 framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4949 _editor->snap_to_with_modifier (pf, event);
4950 pf -= snap_delta (event->button.state);
4952 if (pf > rv->region()->position()) {
4953 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4956 show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4960 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4962 /* this may have been a single click, no drag. We still want the dialog
4963 to show up in that case, so that the user can manually edit the
4964 parameters for the timestretch.
4967 float fraction = 1.0;
4969 if (movement_occurred) {
4971 motion (event, false);
4973 _primary->get_time_axis_view().hide_timestretch ();
4975 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4977 if (adjusted_frame_pos < _primary->region()->position()) {
4978 /* backwards drag of the left edge - not usable */
4982 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4984 fraction = (double) newlen / (double) _primary->region()->length();
4986 #ifndef USE_RUBBERBAND
4987 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4988 if (_primary->region()->data_type() == DataType::AUDIO) {
4989 fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4994 if (!_editor->get_selection().regions.empty()) {
4995 /* primary will already be included in the selection, and edit
4996 group shared editing will propagate selection across
4997 equivalent regions, so just use the current region
5001 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5002 error << _("An error occurred while executing time stretch operation") << endmsg;
5008 TimeFXDrag::aborted (bool)
5010 _primary->get_time_axis_view().hide_timestretch ();
5013 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5016 DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5020 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5022 Drag::start_grab (event);
5026 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5028 _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5032 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5034 if (movement_occurred && _editor->session()) {
5035 /* make sure we stop */
5036 _editor->session()->request_transport_speed (0.0);
5041 ScrubDrag::aborted (bool)
5046 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5050 , _time_selection_at_start (!_editor->get_selection().time.empty())
5052 DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5054 if (_time_selection_at_start) {
5055 start_at_start = _editor->get_selection().time.start();
5056 end_at_start = _editor->get_selection().time.end_frame();
5061 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5063 if (_editor->session() == 0) {
5067 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5069 switch (_operation) {
5070 case CreateSelection:
5071 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5076 cursor = _editor->cursors()->selector;
5077 Drag::start_grab (event, cursor);
5080 case SelectionStartTrim:
5081 if (_editor->clicked_axisview) {
5082 _editor->clicked_axisview->order_selection_trims (_item, true);
5084 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5087 case SelectionEndTrim:
5088 if (_editor->clicked_axisview) {
5089 _editor->clicked_axisview->order_selection_trims (_item, false);
5091 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5095 Drag::start_grab (event, cursor);
5098 case SelectionExtend:
5099 Drag::start_grab (event, cursor);
5103 if (_operation == SelectionMove) {
5104 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5106 show_verbose_cursor_time (adjusted_current_frame (event));
5111 SelectionDrag::setup_pointer_frame_offset ()
5113 switch (_operation) {
5114 case CreateSelection:
5115 _pointer_frame_offset = 0;
5118 case SelectionStartTrim:
5120 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5123 case SelectionEndTrim:
5124 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5127 case SelectionExtend:
5133 SelectionDrag::motion (GdkEvent* event, bool first_move)
5135 framepos_t start = 0;
5137 framecnt_t length = 0;
5138 framecnt_t distance = 0;
5140 framepos_t const pending_position = adjusted_current_frame (event);
5142 if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5146 switch (_operation) {
5147 case CreateSelection:
5149 framepos_t grab = grab_frame ();
5152 grab = adjusted_current_frame (event, false);
5153 if (grab < pending_position) {
5154 _editor->snap_to (grab, RoundDownMaybe);
5156 _editor->snap_to (grab, RoundUpMaybe);
5160 if (pending_position < grab) {
5161 start = pending_position;
5164 end = pending_position;
5168 /* first drag: Either add to the selection
5169 or create a new selection
5176 /* adding to the selection */
5177 _editor->set_selected_track_as_side_effect (Selection::Add);
5178 _editor->clicked_selection = _editor->selection->add (start, end);
5185 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5186 _editor->set_selected_track_as_side_effect (Selection::Set);
5189 _editor->clicked_selection = _editor->selection->set (start, end);
5193 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5194 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5195 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5197 _editor->selection->add (atest);
5201 /* select all tracks within the rectangle that we've marked out so far */
5202 TrackViewList new_selection;
5203 TrackViewList& all_tracks (_editor->track_views);
5205 ArdourCanvas::Coord const top = grab_y();
5206 ArdourCanvas::Coord const bottom = current_pointer_y();
5208 if (top >= 0 && bottom >= 0) {
5210 //first, find the tracks that are covered in the y range selection
5211 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5212 if ((*i)->covered_by_y_range (top, bottom)) {
5213 new_selection.push_back (*i);
5217 //now find any tracks that are GROUPED with the tracks we selected
5218 TrackViewList grouped_add = new_selection;
5219 for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5220 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5221 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5222 for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5223 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5224 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5225 grouped_add.push_back (*j);
5230 //now compare our list with the current selection, and add or remove as necessary
5231 //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5232 TrackViewList tracks_to_add;
5233 TrackViewList tracks_to_remove;
5234 for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5235 if ( !_editor->selection->tracks.contains ( *i ) )
5236 tracks_to_add.push_back ( *i );
5237 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5238 if ( !grouped_add.contains ( *i ) )
5239 tracks_to_remove.push_back ( *i );
5240 _editor->selection->add(tracks_to_add);
5241 _editor->selection->remove(tracks_to_remove);
5247 case SelectionStartTrim:
5249 end = _editor->selection->time[_editor->clicked_selection].end;
5251 if (pending_position > end) {
5254 start = pending_position;
5258 case SelectionEndTrim:
5260 start = _editor->selection->time[_editor->clicked_selection].start;
5262 if (pending_position < start) {
5265 end = pending_position;
5272 start = _editor->selection->time[_editor->clicked_selection].start;
5273 end = _editor->selection->time[_editor->clicked_selection].end;
5275 length = end - start;
5276 distance = pending_position - start;
5277 start = pending_position;
5278 _editor->snap_to (start);
5280 end = start + length;
5284 case SelectionExtend:
5289 switch (_operation) {
5291 if (_time_selection_at_start) {
5292 _editor->selection->move_time (distance);
5296 _editor->selection->replace (_editor->clicked_selection, start, end);
5300 if (_operation == SelectionMove) {
5301 show_verbose_cursor_time(start);
5303 show_verbose_cursor_time(pending_position);
5308 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5310 Session* s = _editor->session();
5312 _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5313 if (movement_occurred) {
5314 motion (event, false);
5315 /* XXX this is not object-oriented programming at all. ick */
5316 if (_editor->selection->time.consolidate()) {
5317 _editor->selection->TimeChanged ();
5320 /* XXX what if its a music time selection? */
5322 if (s->get_play_range() && s->transport_rolling()) {
5323 s->request_play_range (&_editor->selection->time, true);
5324 } else if (!s->config.get_external_sync()) {
5325 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5326 if (_operation == SelectionEndTrim)
5327 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5329 s->request_locate (_editor->get_selection().time.start());
5333 if (_editor->get_selection().time.length() != 0) {
5334 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5336 s->clear_range_selection ();
5341 /* just a click, no pointer movement.
5344 if (was_double_click()) {
5345 if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5346 _editor->temporal_zoom_selection (Both);
5351 if (_operation == SelectionExtend) {
5352 if (_time_selection_at_start) {
5353 framepos_t pos = adjusted_current_frame (event, false);
5354 framepos_t start = min (pos, start_at_start);
5355 framepos_t end = max (pos, end_at_start);
5356 _editor->selection->set (start, end);
5359 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5360 if (_editor->clicked_selection) {
5361 _editor->selection->remove (_editor->clicked_selection);
5364 if (!_editor->clicked_selection) {
5365 _editor->selection->clear_time();
5370 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5371 _editor->selection->set (_editor->clicked_axisview);
5374 if (s && s->get_play_range () && s->transport_rolling()) {
5375 s->request_stop (false, false);
5380 _editor->stop_canvas_autoscroll ();
5381 _editor->clicked_selection = 0;
5382 _editor->commit_reversible_selection_op ();
5386 SelectionDrag::aborted (bool)
5391 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5392 : Drag (e, i, false),
5396 DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5398 _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5399 ArdourCanvas::Rect (0.0, 0.0, 0.0,
5400 physical_screen_height (_editor->current_toplevel()->get_window())));
5401 _drag_rect->hide ();
5403 _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5404 _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5407 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5409 /* normal canvas items will be cleaned up when their parent group is deleted. But
5410 this item is created as the child of a long-lived parent group, and so we
5411 need to explicitly delete it.
5417 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5419 if (_editor->session() == 0) {
5423 Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5425 if (!_editor->temp_location) {
5426 _editor->temp_location = new Location (*_editor->session());
5429 switch (_operation) {
5430 case CreateSkipMarker:
5431 case CreateRangeMarker:
5432 case CreateTransportMarker:
5433 case CreateCDMarker:
5435 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5440 cursor = _editor->cursors()->selector;
5444 Drag::start_grab (event, cursor);
5446 show_verbose_cursor_time (adjusted_current_frame (event));
5450 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5452 framepos_t start = 0;
5454 ArdourCanvas::Rectangle *crect;
5456 switch (_operation) {
5457 case CreateSkipMarker:
5458 crect = _editor->range_bar_drag_rect;
5460 case CreateRangeMarker:
5461 crect = _editor->range_bar_drag_rect;
5463 case CreateTransportMarker:
5464 crect = _editor->transport_bar_drag_rect;
5466 case CreateCDMarker:
5467 crect = _editor->cd_marker_bar_drag_rect;
5470 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5475 framepos_t const pf = adjusted_current_frame (event);
5477 if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5478 framepos_t grab = grab_frame ();
5479 _editor->snap_to (grab);
5481 if (pf < grab_frame()) {
5489 /* first drag: Either add to the selection
5490 or create a new selection.
5495 _editor->temp_location->set (start, end);
5499 update_item (_editor->temp_location);
5501 //_drag_rect->raise_to_top();
5507 _editor->temp_location->set (start, end);
5509 double x1 = _editor->sample_to_pixel (start);
5510 double x2 = _editor->sample_to_pixel (end);
5514 update_item (_editor->temp_location);
5517 show_verbose_cursor_time (pf);
5522 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5524 Location * newloc = 0;
5528 if (movement_occurred) {
5529 motion (event, false);
5532 switch (_operation) {
5533 case CreateSkipMarker:
5534 case CreateRangeMarker:
5535 case CreateCDMarker:
5537 XMLNode &before = _editor->session()->locations()->get_state();
5538 if (_operation == CreateSkipMarker) {
5539 _editor->begin_reversible_command (_("new skip marker"));
5540 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5541 flags = Location::IsRangeMarker | Location::IsSkip;
5542 _editor->range_bar_drag_rect->hide();
5543 } else if (_operation == CreateCDMarker) {
5544 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5545 _editor->begin_reversible_command (_("new CD marker"));
5546 flags = Location::IsRangeMarker | Location::IsCDMarker;
5547 _editor->cd_marker_bar_drag_rect->hide();
5549 _editor->begin_reversible_command (_("new skip marker"));
5550 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5551 flags = Location::IsRangeMarker;
5552 _editor->range_bar_drag_rect->hide();
5554 newloc = new Location (
5555 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5558 _editor->session()->locations()->add (newloc, true);
5559 XMLNode &after = _editor->session()->locations()->get_state();
5560 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5561 _editor->commit_reversible_command ();
5565 case CreateTransportMarker:
5566 // popup menu to pick loop or punch
5567 _editor->new_transport_marker_context_menu (&event->button, _item);
5573 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5575 if (_operation == CreateTransportMarker) {
5577 /* didn't drag, so just locate */
5579 _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5581 } else if (_operation == CreateCDMarker) {
5583 /* didn't drag, but mark is already created so do
5586 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5591 _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5593 if (end == max_framepos) {
5594 end = _editor->session()->current_end_frame ();
5597 if (start == max_framepos) {
5598 start = _editor->session()->current_start_frame ();
5601 switch (_editor->mouse_mode) {
5603 /* find the two markers on either side and then make the selection from it */
5604 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5608 /* find the two markers on either side of the click and make the range out of it */
5609 _editor->selection->set (start, end);
5618 _editor->stop_canvas_autoscroll ();
5622 RangeMarkerBarDrag::aborted (bool movement_occurred)
5624 if (movement_occurred) {
5625 _drag_rect->hide ();
5630 RangeMarkerBarDrag::update_item (Location* location)
5632 double const x1 = _editor->sample_to_pixel (location->start());
5633 double const x2 = _editor->sample_to_pixel (location->end());
5635 _drag_rect->set_x0 (x1);
5636 _drag_rect->set_x1 (x2);
5639 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5641 , _cumulative_dx (0)
5642 , _cumulative_dy (0)
5643 , _was_selected (false)
5645 DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5647 _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5649 _region = &_primary->region_view ();
5650 _note_height = _region->midi_stream_view()->note_height ();
5654 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5656 Drag::start_grab (event);
5657 setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5659 if (!(_was_selected = _primary->selected())) {
5661 /* tertiary-click means extend selection - we'll do that on button release,
5662 so don't add it here, because otherwise we make it hard to figure
5663 out the "extend-to" range.
5666 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5669 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5672 _region->note_selected (_primary, true);
5674 _editor->get_selection().clear_points();
5675 _region->unique_select (_primary);
5681 /** @return Current total drag x change in frames */
5683 NoteDrag::total_dx (const guint state) const
5685 if (_x_constrained) {
5688 TempoMap& map (_editor->session()->tempo_map());
5691 frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5693 /* primary note time */
5694 double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5695 frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5697 /* new time of the primary note in session frames */
5698 frameoffset_t st = n + dx + snap_delta (state);
5700 framepos_t const rp = _region->region()->position ();
5702 /* prevent the note being dragged earlier than the region's position */
5705 /* possibly snap and return corresponding delta */
5709 if (ArdourKeyboard::indicates_snap (state)) {
5710 if (_editor->snap_mode () != SnapOff) {
5714 if (_editor->snap_mode () == SnapOff) {
5716 /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5717 if (ArdourKeyboard::indicates_snap_delta (state)) {
5725 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5726 ret = _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5728 ret = st - n - snap_delta (state);
5733 /** @return Current total drag y change in note number */
5735 NoteDrag::total_dy () const
5737 if (_y_constrained) {
5741 double const y = _region->midi_view()->y_position ();
5742 /* new current note */
5743 uint8_t n = _region->y_to_note (current_pointer_y () - y);
5745 MidiStreamView* msv = _region->midi_stream_view ();
5746 n = max (msv->lowest_note(), n);
5747 n = min (msv->highest_note(), n);
5748 /* and work out delta */
5749 return n - _region->y_to_note (grab_y() - y);
5753 NoteDrag::motion (GdkEvent * event, bool)
5755 /* Total change in x and y since the start of the drag */
5756 frameoffset_t const dx = total_dx (event->button.state);
5757 int8_t const dy = total_dy ();
5759 /* Now work out what we have to do to the note canvas items to set this new drag delta */
5760 double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5761 double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5764 _cumulative_dx += tdx;
5765 _cumulative_dy += tdy;
5767 int8_t note_delta = total_dy();
5770 _region->move_selection (tdx, tdy, note_delta);
5772 /* the new note value may be the same as the old one, but we
5773 * don't know what that means because the selection may have
5774 * involved more than one note and we might be doing something
5775 * odd with them. so show the note value anyway, always.
5778 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5780 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5786 NoteDrag::finished (GdkEvent* ev, bool moved)
5789 /* no motion - select note */
5791 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5792 _editor->current_mouse_mode() == Editing::MouseDraw) {
5794 bool changed = false;
5796 if (_was_selected) {
5797 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5799 _region->note_deselected (_primary);
5802 _editor->get_selection().clear_points();
5803 _region->unique_select (_primary);
5807 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5808 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5810 if (!extend && !add && _region->selection_size() > 1) {
5811 _editor->get_selection().clear_points();
5812 _region->unique_select (_primary);
5814 } else if (extend) {
5815 _region->note_selected (_primary, true, true);
5818 /* it was added during button press */
5825 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5826 _editor->commit_reversible_selection_op();
5830 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5835 NoteDrag::aborted (bool)
5840 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5841 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5842 : Drag (editor, atv->base_item ())
5844 , _y_origin (atv->y_position())
5845 , _nothing_to_drag (false)
5847 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5848 setup (atv->lines ());
5851 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5852 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5853 : Drag (editor, rv->get_canvas_group ())
5855 , _y_origin (rv->get_time_axis_view().y_position())
5856 , _nothing_to_drag (false)
5859 DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5861 list<boost::shared_ptr<AutomationLine> > lines;
5863 AudioRegionView* audio_view;
5864 AutomationRegionView* automation_view;
5865 if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5866 lines.push_back (audio_view->get_gain_line ());
5867 } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5868 lines.push_back (automation_view->line ());
5871 error << _("Automation range drag created for invalid region type") << endmsg;
5877 /** @param lines AutomationLines to drag.
5878 * @param offset Offset from the session start to the points in the AutomationLines.
5881 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5883 /* find the lines that overlap the ranges being dragged */
5884 list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5885 while (i != lines.end ()) {
5886 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5889 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5891 /* check this range against all the AudioRanges that we are using */
5892 list<AudioRange>::const_iterator k = _ranges.begin ();
5893 while (k != _ranges.end()) {
5894 if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5900 /* add it to our list if it overlaps at all */
5901 if (k != _ranges.end()) {
5906 _lines.push_back (n);
5912 /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5916 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5918 return 1.0 - ((global_y - _y_origin) / line->height());
5922 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5924 const double v = list->eval(x);
5925 return _integral ? rint(v) : v;
5929 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5931 Drag::start_grab (event, cursor);
5933 /* Get line states before we start changing things */
5934 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5935 i->state = &i->line->get_state ();
5936 i->original_fraction = y_fraction (i->line, current_pointer_y());
5939 if (_ranges.empty()) {
5941 /* No selected time ranges: drag all points */
5942 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5943 uint32_t const N = i->line->npoints ();
5944 for (uint32_t j = 0; j < N; ++j) {
5945 i->points.push_back (i->line->nth (j));
5951 if (_nothing_to_drag) {
5957 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5959 if (_nothing_to_drag && !first_move) {
5964 _editor->begin_reversible_command (_("automation range move"));
5966 if (!_ranges.empty()) {
5968 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5970 framecnt_t const half = (i->start + i->end) / 2;
5972 /* find the line that this audio range starts in */
5973 list<Line>::iterator j = _lines.begin();
5974 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5978 if (j != _lines.end()) {
5979 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5981 /* j is the line that this audio range starts in; fade into it;
5982 64 samples length plucked out of thin air.
5985 framepos_t a = i->start + 64;
5990 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5991 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5993 XMLNode &before = the_list->get_state();
5994 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5995 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5997 if (add_p || add_q) {
5998 _editor->session()->add_command (
5999 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6003 /* same thing for the end */
6006 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6010 if (j != _lines.end()) {
6011 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6013 /* j is the line that this audio range starts in; fade out of it;
6014 64 samples length plucked out of thin air.
6017 framepos_t b = i->end - 64;
6022 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6023 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6025 XMLNode &before = the_list->get_state();
6026 bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6027 bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6029 if (add_p || add_q) {
6030 _editor->session()->add_command (
6031 new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6036 _nothing_to_drag = true;
6038 /* Find all the points that should be dragged and put them in the relevant
6039 points lists in the Line structs.
6042 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6044 uint32_t const N = i->line->npoints ();
6045 for (uint32_t j = 0; j < N; ++j) {
6047 /* here's a control point on this line */
6048 ControlPoint* p = i->line->nth (j);
6049 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6051 /* see if it's inside a range */
6052 list<AudioRange>::const_iterator k = _ranges.begin ();
6053 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6057 if (k != _ranges.end()) {
6058 /* dragging this point */
6059 _nothing_to_drag = false;
6060 i->points.push_back (p);
6066 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6067 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6071 for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6072 float const f = y_fraction (l->line, current_pointer_y());
6073 /* we are ignoring x position for this drag, so we can just pass in anything */
6074 pair<double, float> result;
6076 result = l->line->drag_motion (0, f, true, false, ignored);
6077 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6082 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6084 if (_nothing_to_drag || !motion_occurred) {
6088 motion (event, false);
6089 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6090 i->line->end_drag (false, 0);
6093 _editor->commit_reversible_command ();
6097 AutomationRangeDrag::aborted (bool)
6099 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6104 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6106 , initial_time_axis_view (itav)
6108 /* note that time_axis_view may be null if the regionview was created
6109 * as part of a copy operation.
6111 time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6112 layer = v->region()->layer ();
6113 initial_y = v->get_canvas_group()->position().y;
6114 initial_playlist = v->region()->playlist ();
6115 initial_position = v->region()->position ();
6116 initial_end = v->region()->position () + v->region()->length ();
6119 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6120 : Drag (e, i->canvas_item ())
6123 , _cumulative_dx (0)
6125 DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6126 _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6131 PatchChangeDrag::motion (GdkEvent* ev, bool)
6133 framepos_t f = adjusted_current_frame (ev);
6134 boost::shared_ptr<Region> r = _region_view->region ();
6135 f = max (f, r->position ());
6136 f = min (f, r->last_frame ());
6138 framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6139 double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6140 _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6141 _cumulative_dx = dxu;
6145 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6147 if (!movement_occurred) {
6148 if (was_double_click()) {
6149 _region_view->edit_patch_change (_patch_change);
6154 boost::shared_ptr<Region> r (_region_view->region ());
6155 framepos_t f = adjusted_current_frame (ev);
6156 f = max (f, r->position ());
6157 f = min (f, r->last_frame ());
6159 _region_view->move_patch_change (
6161 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6166 PatchChangeDrag::aborted (bool)
6168 _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6172 PatchChangeDrag::setup_pointer_frame_offset ()
6174 boost::shared_ptr<Region> region = _region_view->region ();
6175 _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6178 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6179 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6186 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6188 _region_view->update_drag_selection (
6190 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6194 MidiRubberbandSelectDrag::deselect_things ()
6199 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6200 : RubberbandSelectDrag (e, rv->get_canvas_group ())
6203 _vertical_only = true;
6207 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6209 double const y = _region_view->midi_view()->y_position ();
6211 y1 = max (0.0, y1 - y);
6212 y2 = max (0.0, y2 - y);
6214 _region_view->update_vertical_drag_selection (
6217 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6222 MidiVerticalSelectDrag::deselect_things ()
6227 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6228 : RubberbandSelectDrag (e, i)
6234 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6236 if (drag_in_progress) {
6237 /* We just want to select things at the end of the drag, not during it */
6241 Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6243 _editor->begin_reversible_selection_op (X_("rubberband selection"));
6245 _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6247 _editor->commit_reversible_selection_op ();
6251 EditorRubberbandSelectDrag::deselect_things ()
6253 _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6255 _editor->selection->clear_tracks();
6256 _editor->selection->clear_regions();
6257 _editor->selection->clear_points ();
6258 _editor->selection->clear_lines ();
6259 _editor->selection->clear_midi_notes ();
6261 _editor->commit_reversible_selection_op();
6264 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6269 _note[0] = _note[1] = 0;
6272 NoteCreateDrag::~NoteCreateDrag ()
6278 NoteCreateDrag::grid_frames (framepos_t t) const
6281 const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6282 const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6284 return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6285 - _region_view->region_beats_to_region_frames (t_beats);
6289 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6291 Drag::start_grab (event, cursor);
6293 _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6294 TempoMap& map (_editor->session()->tempo_map());
6296 const framepos_t pf = _drags->current_pointer_frame ();
6297 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6299 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6301 double eqaf = map.exact_qn_at_frame (pf, divisions);
6303 if (divisions != 0) {
6305 const double qaf = map.quarter_note_at_frame (pf);
6307 /* Hack so that we always snap to the note that we are over, instead of snapping
6308 to the next one if we're more than halfway through the one we're over.
6311 const double rem = eqaf - qaf;
6313 eqaf -= grid_beats.to_double();
6317 _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6318 /* minimum initial length is grid beats */
6319 _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6321 double const x0 = _editor->sample_to_pixel (_note[0]);
6322 double const x1 = _editor->sample_to_pixel (_note[1]);
6323 double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6325 _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6326 _drag_rect->set_outline_all ();
6327 _drag_rect->set_outline_color (0xffffff99);
6328 _drag_rect->set_fill_color (0xffffff66);
6332 NoteCreateDrag::motion (GdkEvent* event, bool)
6334 TempoMap& map (_editor->session()->tempo_map());
6335 const framepos_t pf = _drags->current_pointer_frame ();
6336 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6337 double eqaf = map.exact_qn_at_frame (pf, divisions);
6339 if (divisions != 0) {
6341 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6343 const double qaf = map.quarter_note_at_frame (pf);
6344 /* Hack so that we always snap to the note that we are over, instead of snapping
6345 to the next one if we're more than halfway through the one we're over.
6348 const double rem = eqaf - qaf;
6350 eqaf -= grid_beats.to_double();
6353 eqaf += grid_beats.to_double();
6355 _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6357 double const x0 = _editor->sample_to_pixel (_note[0]);
6358 double const x1 = _editor->sample_to_pixel (_note[1]);
6359 _drag_rect->set_x0 (std::min(x0, x1));
6360 _drag_rect->set_x1 (std::max(x0, x1));
6364 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6366 /* we create a note even if there was no movement */
6367 framepos_t const start = min (_note[0], _note[1]);
6368 framepos_t const start_sess_rel = start + _region_view->region()->position();
6369 framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6370 framecnt_t const g = grid_frames (start);
6372 if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6376 TempoMap& map (_editor->session()->tempo_map());
6377 const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6378 Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6380 _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6384 NoteCreateDrag::y_to_region (double y) const
6387 _region_view->get_canvas_group()->canvas_to_item (x, y);
6392 NoteCreateDrag::aborted (bool)
6397 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6405 HitCreateDrag::~HitCreateDrag ()
6410 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6412 Drag::start_grab (event, cursor);
6414 TempoMap& map (_editor->session()->tempo_map());
6416 const framepos_t pf = _drags->current_pointer_frame ();
6417 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6419 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6421 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6423 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6427 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6428 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6430 Evoral::Beats length = _region_view->get_grid_beats (pf);
6432 _region_view->create_note_at (start, y, length, event->button.state, false);
6439 HitCreateDrag::motion (GdkEvent* event, bool)
6441 TempoMap& map (_editor->session()->tempo_map());
6443 const framepos_t pf = _drags->current_pointer_frame ();
6444 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6446 if (divisions == 0) {
6450 const double eqaf = map.exact_qn_at_frame (pf, divisions);
6451 const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6452 const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6454 if (_last_pos == start && y == _last_y) {
6458 Evoral::Beats length = _region_view->get_grid_beats (pf);
6460 boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6461 if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6465 _region_view->create_note_at (start, y, length, event->button.state, false);
6472 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6478 HitCreateDrag::y_to_region (double y) const
6481 _region_view->get_canvas_group()->canvas_to_item (x, y);
6486 HitCreateDrag::aborted (bool)
6491 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6496 std::cout << ("CrossfadeEdgeDrag is DEPRECATED. See TrimDrag::preserve_fade_anchor") << endl;
6500 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6502 Drag::start_grab (event, cursor);
6506 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6512 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6515 distance = _drags->current_pointer_x() - grab_x();
6516 len = ar->fade_in()->back()->when;
6518 distance = grab_x() - _drags->current_pointer_x();
6519 len = ar->fade_out()->back()->when;
6522 /* how long should it be ? */
6524 new_length = len + _editor->pixel_to_sample (distance);
6526 /* now check with the region that this is legal */
6528 new_length = ar->verify_xfade_bounds (new_length, start);
6531 arv->reset_fade_in_shape_width (ar, new_length);
6533 arv->reset_fade_out_shape_width (ar, new_length);
6538 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6544 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6547 distance = _drags->current_pointer_x() - grab_x();
6548 len = ar->fade_in()->back()->when;
6550 distance = grab_x() - _drags->current_pointer_x();
6551 len = ar->fade_out()->back()->when;
6554 new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6556 _editor->begin_reversible_command ("xfade trim");
6557 ar->playlist()->clear_owned_changes ();
6560 ar->set_fade_in_length (new_length);
6562 ar->set_fade_out_length (new_length);
6565 /* Adjusting the xfade may affect other regions in the playlist, so we need
6566 to get undo Commands from the whole playlist rather than just the
6570 vector<Command*> cmds;
6571 ar->playlist()->rdiff (cmds);
6572 _editor->session()->add_commands (cmds);
6573 _editor->commit_reversible_command ();
6578 CrossfadeEdgeDrag::aborted (bool)
6581 // arv->redraw_start_xfade ();
6583 // arv->redraw_end_xfade ();
6587 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6588 : Drag (e, item, true)
6589 , line (new EditorCursor (*e))
6591 line->set_position (pos);
6593 line->track_canvas_item().reparent (_editor->_drag_motion_group);
6596 RegionCutDrag::~RegionCutDrag ()
6602 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6604 Drag::start_grab (event, c);
6605 motion (event, false);
6609 RegionCutDrag::motion (GdkEvent* event, bool)
6611 framepos_t pos = _drags->current_pointer_frame();
6612 _editor->snap_to_with_modifier (pos, event);
6614 line->set_position (pos);
6618 RegionCutDrag::finished (GdkEvent* event, bool)
6620 _editor->get_track_canvas()->canvas()->re_enter();
6622 framepos_t pos = _drags->current_pointer_frame();
6623 _editor->snap_to_with_modifier (pos, event);
6627 RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6633 _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state),
6638 RegionCutDrag::aborted (bool)
6642 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6643 : Drag (e, item, true)
6648 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6650 Drag::start_grab (event, c);
6652 framepos_t where = _editor->canvas_event_sample(event);
6654 _editor->_dragging_playhead = true;
6656 _editor->playhead_cursor->set_position (where);
6660 RulerZoomDrag::motion (GdkEvent* event, bool)
6662 framepos_t where = _editor->canvas_event_sample(event);
6664 _editor->playhead_cursor->set_position (where);
6666 const double movement_limit = 20.0;
6667 const double scale = 1.08;
6668 const double y_delta = last_pointer_y() - current_pointer_y();
6670 if (y_delta > 0 && y_delta < movement_limit) {
6671 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6672 } else if (y_delta < 0 && y_delta > -movement_limit) {
6673 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6678 RulerZoomDrag::finished (GdkEvent*, bool)
6680 _editor->_dragging_playhead = false;
6682 Session* s = _editor->session ();
6684 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6685 _editor->_pending_locate_request = true;
6691 RulerZoomDrag::aborted (bool)