Allow to override A/V-lock (when moving audio)
[ardour.git] / gtk2_ardour / editor_drag.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <stdint.h>
25 #include <algorithm>
26
27 #include "pbd/memento_command.h"
28 #include "pbd/basename.h"
29 #include "pbd/stateful_diff_command.h"
30
31 #include "gtkmm2ext/utils.h"
32
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"
42
43 #include "canvas/canvas.h"
44 #include "canvas/scroll_group.h"
45
46 #include "editor.h"
47 #include "i18n.h"
48 #include "keyboard.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"
62 #include "debug.h"
63 #include "editor_cursors.h"
64 #include "mouse_cursors.h"
65 #include "note_base.h"
66 #include "patch_change.h"
67 #include "verbose_cursor.h"
68
69 using namespace std;
70 using namespace ARDOUR;
71 using namespace PBD;
72 using namespace Gtk;
73 using namespace Gtkmm2ext;
74 using namespace Editing;
75 using namespace ArdourCanvas;
76
77 using Gtkmm2ext::Keyboard;
78
79 double ControlPointDrag::_zero_gain_fraction = -1.0;
80
81 DragManager::DragManager (Editor* e)
82         : _editor (e)
83         , _ending (false)
84         , _current_pointer_frame (0)
85 {
86 }
87
88 DragManager::~DragManager ()
89 {
90         abort ();
91 }
92
93 /** Call abort for each active drag */
94 void
95 DragManager::abort ()
96 {
97         _ending = true;
98
99         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
100                 (*i)->abort ();
101                 delete *i;
102         }
103
104         if (!_drags.empty ()) {
105                 _editor->set_follow_playhead (_old_follow_playhead, false);
106         }
107
108         _drags.clear ();
109         _editor->abort_reversible_command();
110
111         _ending = false;
112 }
113
114 void
115 DragManager::add (Drag* d)
116 {
117         d->set_manager (this);
118         _drags.push_back (d);
119 }
120
121 void
122 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
123 {
124         d->set_manager (this);
125         _drags.push_back (d);
126         start_grab (e, c);
127 }
128
129 void
130 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
131 {
132         /* Prevent follow playhead during the drag to be nice to the user */
133         _old_follow_playhead = _editor->follow_playhead ();
134         _editor->set_follow_playhead (false);
135
136         _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
137
138         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
139                 (*i)->start_grab (e, c);
140         }
141 }
142
143 /** Call end_grab for each active drag.
144  *  @return true if any drag reported movement having occurred.
145  */
146 bool
147 DragManager::end_grab (GdkEvent* e)
148 {
149         _ending = true;
150
151         bool r = false;
152         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
153                 bool const t = (*i)->end_grab (e);
154                 if (t) {
155                         r = true;
156                 }
157                 delete *i;
158         }
159
160         _drags.clear ();
161
162         _ending = false;
163
164         _editor->set_follow_playhead (_old_follow_playhead, false);
165
166         return r;
167 }
168
169 void
170 DragManager::mark_double_click ()
171 {
172         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
173                 (*i)->set_double_click (true);
174         }
175 }
176
177 bool
178 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
179 {
180         bool r = false;
181
182         /* calling this implies that we expect the event to have canvas
183          * coordinates
184          *
185          * Can we guarantee that this is true?
186          */
187
188         _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
189
190         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
191                 bool const t = (*i)->motion_handler (e, from_autoscroll);
192                 /* run all handlers; return true if at least one of them
193                    returns true (indicating that the event has been handled).
194                 */
195                 if (t) {
196                         r = true;
197                 }
198
199         }
200
201         return r;
202 }
203
204 bool
205 DragManager::have_item (ArdourCanvas::Item* i) const
206 {
207         list<Drag*>::const_iterator j = _drags.begin ();
208         while (j != _drags.end() && (*j)->item () != i) {
209                 ++j;
210         }
211
212         return j != _drags.end ();
213 }
214
215 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
216         : _editor (e)
217         , _item (i)
218         , _pointer_frame_offset (0)
219         , _x_constrained (false)
220         , _y_constrained (false)
221         , _trackview_only (trackview_only)
222         , _move_threshold_passed (false)
223         , _starting_point_passed (false)
224         , _initially_vertical (false)
225         , _was_double_click (false)
226         , _raw_grab_frame (0)
227         , _grab_frame (0)
228         , _last_pointer_frame (0)
229         , _snap_delta (0)
230 {
231
232 }
233
234 void
235 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
236 {
237         _item->ungrab ();
238         _item = new_item;
239
240         if (!_cursor_ctx) {
241                 _cursor_ctx = CursorContext::create (*_editor, cursor);
242         } else {
243                 _cursor_ctx->change (cursor);
244         }
245
246         _item->grab ();
247 }
248
249 void
250 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
251 {
252
253         /* we set up x/y dragging constraints on first move */
254
255         _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
256
257         setup_pointer_frame_offset ();
258         _grab_frame = adjusted_frame (_raw_grab_frame, event);
259         _last_pointer_frame = _grab_frame;
260         _last_pointer_x = _grab_x;
261
262         if (_trackview_only) {
263                 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
264         }
265
266         _last_pointer_y = _grab_y;
267
268         _item->grab ();
269
270         if (!_editor->cursors()->is_invalid (cursor)) {
271                 /* CAIROCANVAS need a variant here that passes *cursor */
272                 _cursor_ctx = CursorContext::create (*_editor, cursor);
273         }
274
275         if (_editor->session() && _editor->session()->transport_rolling()) {
276                 _was_rolling = true;
277         } else {
278                 _was_rolling = false;
279         }
280
281         switch (_editor->snap_type()) {
282         case SnapToRegionStart:
283         case SnapToRegionEnd:
284         case SnapToRegionSync:
285         case SnapToRegionBoundary:
286                 _editor->build_region_boundary_cache ();
287                 break;
288         default:
289                 break;
290         }
291 }
292
293 /** Call to end a drag `successfully'.  Ungrabs item and calls
294  *  subclass' finished() method.
295  *
296  *  @param event GDK event, or 0.
297  *  @return true if some movement occurred, otherwise false.
298  */
299 bool
300 Drag::end_grab (GdkEvent* event)
301 {
302         _editor->stop_canvas_autoscroll ();
303
304         _item->ungrab ();
305
306         finished (event, _move_threshold_passed);
307
308         _editor->verbose_cursor()->hide ();
309         _cursor_ctx.reset();
310
311         return _move_threshold_passed;
312 }
313
314 framepos_t
315 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
316 {
317         framepos_t pos = 0;
318
319         if (f > _pointer_frame_offset) {
320                 pos = f - _pointer_frame_offset;
321         }
322
323         if (snap) {
324                 _editor->snap_to_with_modifier (pos, event);
325         }
326
327         return pos;
328 }
329
330 framepos_t
331 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
332 {
333         return adjusted_frame (_drags->current_pointer_frame (), event, snap);
334 }
335
336 frameoffset_t
337 Drag::snap_delta (guint state) const
338 {
339         if (ArdourKeyboard::indicates_snap_delta (state)) {
340                 return _snap_delta;
341         }
342
343         return 0;
344 }
345
346 double
347 Drag::current_pointer_x() const
348 {
349         return _drags->current_pointer_x ();
350 }
351
352 double
353 Drag::current_pointer_y () const
354 {
355         if (!_trackview_only) {
356                 return _drags->current_pointer_y ();
357         }
358
359         return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
360 }
361
362 void
363 Drag::setup_snap_delta (framepos_t pos)
364 {
365         framepos_t temp = pos;
366         _editor->snap_to (temp, ARDOUR::RoundNearest, false, true);
367         _snap_delta = temp - pos;
368 }
369
370 bool
371 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
372 {
373         /* check to see if we have moved in any way that matters since the last motion event */
374         if (_move_threshold_passed &&
375             (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
376             (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
377                 return false;
378         }
379
380         pair<framecnt_t, int> const threshold = move_threshold ();
381
382         bool const old_move_threshold_passed = _move_threshold_passed;
383
384         if (!_move_threshold_passed) {
385
386                 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
387                 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
388
389                 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
390         }
391
392         if (active (_editor->mouse_mode) && _move_threshold_passed) {
393
394                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
395
396                         if (old_move_threshold_passed != _move_threshold_passed) {
397
398                                 /* just changed */
399
400                                 if (fabs (current_pointer_y() - _grab_y) > fabs (current_pointer_x() - _grab_x)) {
401                                         _initially_vertical = true;
402                                 } else {
403                                         _initially_vertical = false;
404                                 }
405                                 /** check constraints for this drag.
406                                  *  Note that the current convention is to use "contains" for 
407                                  *  key modifiers during motion and "equals" when initiating a drag.
408                                  *  In this case we haven't moved yet, so "equals" applies here.
409                                  */
410                                 if (Config->get_edit_mode() != Lock) {
411                                         if (event->motion.state & Gdk::BUTTON2_MASK) {
412                                                 // if dragging with button2, the motion is x constrained, with constraint modifier it is y constrained
413                                                 if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
414                                                         _x_constrained = false;
415                                                         _y_constrained = true;
416                                                 } else {
417                                                         _x_constrained = true;
418                                                         _y_constrained = false;
419                                                 }
420                                         } else if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
421                                                 // if dragging normally, the motion is constrained to the first direction of movement.
422                                                 if (_initially_vertical) {
423                                                         _x_constrained = true;
424                                                         _y_constrained = false;
425                                                 } else {
426                                                         _x_constrained = false;
427                                                         _y_constrained = true;
428                                                 }
429                                         }
430                                 } else {
431                                         if (event->button.state & Gdk::BUTTON2_MASK) {
432                                                 _x_constrained = false;
433                                         } else {
434                                                 _x_constrained = true;
435                                         }
436                                         _y_constrained = false;
437                                 }
438                         }
439
440                         if (!from_autoscroll) {
441                                 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
442                         }
443
444                         if (!_editor->autoscroll_active() || from_autoscroll) {
445
446
447                                 bool first_move = (_move_threshold_passed != old_move_threshold_passed) || from_autoscroll;
448
449                                 motion (event, first_move && !_starting_point_passed);
450
451                                 if (first_move && !_starting_point_passed) {
452                                         _starting_point_passed = true;
453                                 }
454
455                                 _last_pointer_x = _drags->current_pointer_x ();
456                                 _last_pointer_y = current_pointer_y ();
457                                 _last_pointer_frame = adjusted_current_frame (event);
458                         }
459
460                         return true;
461                 }
462         }
463
464         return false;
465 }
466
467 /** Call to abort a drag.  Ungrabs item and calls subclass's aborted () */
468 void
469 Drag::abort ()
470 {
471         if (_item) {
472                 _item->ungrab ();
473         }
474
475         aborted (_move_threshold_passed);
476
477         _editor->stop_canvas_autoscroll ();
478         _editor->verbose_cursor()->hide ();
479 }
480
481 void
482 Drag::show_verbose_cursor_time (framepos_t frame)
483 {
484         _editor->verbose_cursor()->set_time (frame);
485         _editor->verbose_cursor()->show ();
486 }
487
488 void
489 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
490 {
491         _editor->verbose_cursor()->set_duration (start, end);
492         _editor->verbose_cursor()->show ();
493 }
494
495 void
496 Drag::show_verbose_cursor_text (string const & text)
497 {
498         _editor->verbose_cursor()->set (text);
499         _editor->verbose_cursor()->show ();
500 }
501
502 boost::shared_ptr<Region>
503 Drag::add_midi_region (MidiTimeAxisView* view)
504 {
505         if (_editor->session()) {
506                 const TempoMap& map (_editor->session()->tempo_map());
507                 framecnt_t pos = grab_frame();
508                 const Meter& m = map.meter_at (pos);
509                 /* not that the frame rate used here can be affected by pull up/down which
510                    might be wrong.
511                 */
512                 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
513                 return view->add_region (grab_frame(), len, true);
514         }
515
516         return boost::shared_ptr<Region>();
517 }
518
519 struct EditorOrderTimeAxisViewSorter {
520         bool operator() (TimeAxisView* a, TimeAxisView* b) {
521                 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
522                 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
523                 assert (ra && rb);
524                 return ra->route()->order_key () < rb->route()->order_key ();
525         }
526 };
527
528 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
529         : Drag (e, i)
530         , _primary (p)
531         , _ntracks (0)
532 {
533         _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
534
535         /* Make a list of tracks to refer to during the drag; we include hidden tracks,
536            as some of the regions we are dragging may be on such tracks.
537         */
538
539         TrackViewList track_views = _editor->track_views;
540         track_views.sort (EditorOrderTimeAxisViewSorter ());
541
542         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
543                 _time_axis_views.push_back (*i);
544
545                 TimeAxisView::Children children_list = (*i)->get_child_list ();
546                 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
547                         _time_axis_views.push_back (j->get());
548                 }
549         }
550
551         /* the list of views can be empty at this point if this is a region list-insert drag
552          */
553
554         for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
555                 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
556         }
557
558         RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
559 }
560
561 void
562 RegionDrag::region_going_away (RegionView* v)
563 {
564         list<DraggingView>::iterator i = _views.begin ();
565         while (i != _views.end() && i->view != v) {
566                 ++i;
567         }
568
569         if (i != _views.end()) {
570                 _views.erase (i);
571         }
572 }
573
574 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
575  *  or -1 if it is not found.
576  */
577 int
578 RegionDrag::find_time_axis_view (TimeAxisView* t) const
579 {
580         int i = 0;
581         int const N = _time_axis_views.size ();
582         while (i < N && _time_axis_views[i] != t) {
583                 ++i;
584         }
585
586         if (_time_axis_views[i] != t) {
587                 return -1;
588         }
589
590         return i;
591 }
592
593 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
594         : RegionDrag (e, i, p, v)
595         , _brushing (b)
596         , _ignore_video_lock (false)
597         , _total_x_delta (0)
598         , _last_pointer_time_axis_view (0)
599         , _last_pointer_layer (0)
600         , _ndropzone (0)
601         , _pdropzone (0)
602         , _ddropzone (0)
603 {
604         DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
605 }
606
607 void
608 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
609 {
610         Drag::start_grab (event, cursor);
611         setup_snap_delta (_last_frame_position);
612
613         show_verbose_cursor_time (_last_frame_position);
614
615         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
616         if (tv.first) {
617                 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
618                 assert(_last_pointer_time_axis_view >= 0);
619                 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
620         }
621
622         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
623                 _ignore_video_lock = true;
624         }
625
626         if (_brushing) {
627                 /* cross track dragging seems broken here. disabled for now. */
628                 _y_constrained = true;
629         }
630 }
631
632 double
633 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
634 {
635         /* compute the amount of pointer motion in frames, and where
636            the region would be if we moved it by that much.
637         */
638         *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
639
640         framepos_t sync_frame;
641         framecnt_t sync_offset;
642         int32_t sync_dir;
643
644         sync_offset = _primary->region()->sync_offset (sync_dir);
645
646         /* we don't handle a sync point that lies before zero.
647          */
648         if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
649
650                 sync_frame = *pending_region_position + (sync_dir * sync_offset);
651
652                 _editor->snap_to_with_modifier (sync_frame, event);
653
654                 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
655
656         } else {
657                 *pending_region_position = _last_frame_position;
658         }
659
660         if (*pending_region_position > max_framepos - _primary->region()->length()) {
661                 *pending_region_position = _last_frame_position;
662         }
663
664         double dx = 0;
665
666         bool const x_move_allowed = !_x_constrained;
667
668         if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
669
670                 /* x movement since last time (in pixels) */
671                 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
672
673                 /* total x movement */
674                 framecnt_t total_dx = *pending_region_position;
675                 if (regions_came_from_canvas()) {
676                         total_dx = total_dx - grab_frame ();
677                 }
678
679                 /* check that no regions have gone off the start of the session */
680                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
681                         if ((i->view->region()->position() + total_dx) < 0) {
682                                 dx = 0;
683                                 *pending_region_position = _last_frame_position;
684                                 break;
685                         }
686                 }
687
688         }
689
690         return dx;
691 }
692
693 int
694 RegionDrag::apply_track_delta (const int start, const int delta, const int skip, const bool distance_only) const
695 {
696         if (delta == 0) {
697                 return start;
698         }
699
700         const int tavsize  = _time_axis_views.size();
701         const int dt = delta > 0 ? +1 : -1;
702         int current  = start;
703         int target   = start + delta - skip;
704
705         assert (current < 0 || current >= tavsize || !_time_axis_views[current]->hidden());
706         assert (skip == 0 || (skip < 0 && delta < 0) || (skip > 0 && delta > 0));
707
708         while (current >= 0 && current != target) {
709                 current += dt;
710                 if (current < 0 && dt < 0) {
711                         break;
712                 }
713                 if (current >= tavsize && dt > 0) {
714                         break;
715                 }
716                 if (current < 0 || current >= tavsize) {
717                         continue;
718                 }
719
720                 RouteTimeAxisView const * rtav = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[current]);
721                 if (_time_axis_views[current]->hidden() || !rtav || !rtav->is_track()) {
722                         target += dt;
723                 }
724
725                 if (distance_only && current == start + delta) {
726                         break;
727                 }
728         }
729         return target;
730 }
731
732 bool
733 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
734 {
735         if (_y_constrained) {
736                 return false;
737         }
738
739         const int tavsize  = _time_axis_views.size();
740         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
741                 int n = apply_track_delta (i->time_axis_view, delta_track, skip_invisible);
742                 assert (n < 0 || n >= tavsize || !_time_axis_views[n]->hidden());
743
744                 if (i->time_axis_view < 0 || i->time_axis_view >= tavsize) {
745                         /* already in the drop zone */
746                         if (delta_track >= 0) {
747                                 /* downward motion - OK if others are still not in the dropzone */
748                                 continue;
749                         }
750
751                 }
752
753                 if (n < 0) {
754                         /* off the top */
755                         return false;
756                 } else if (n >= tavsize) {
757                         /* downward motion into drop zone. That's fine. */
758                         continue;
759                 }
760
761                 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
762                 if (to == 0 || to->hidden() || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
763                         /* not a track, or the wrong type */
764                         return false;
765                 }
766
767                 double const l = i->layer + delta_layer;
768
769                 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
770                    mode to allow the user to place a region below another on layer 0.
771                 */
772                 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
773                         /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
774                            If it has, the layers will be munged later anyway, so it's ok.
775                         */
776                         return false;
777                 }
778         }
779
780         /* all regions being dragged are ok with this change */
781         return true;
782 }
783
784 struct DraggingViewSorter {
785         bool operator() (const DraggingView& a, const DraggingView& b) {
786                 return a.time_axis_view < b.time_axis_view;
787         }
788 };
789
790 void
791 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
792 {
793         double delta_layer = 0;
794         int delta_time_axis_view = 0;
795         int current_pointer_time_axis_view = -1;
796
797         assert (!_views.empty ());
798
799         /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
800
801         /* Find the TimeAxisView that the pointer is now over */
802         const double cur_y = current_pointer_y ();
803         pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (cur_y);
804         TimeAxisView* tv = r.first;
805
806         if (!tv && cur_y < 0) {
807                 /* above trackview area, autoscroll hasn't moved us since last time, nothing to do */
808                 return;
809         }
810
811         /* find drop-zone y-position */
812         Coord last_track_bottom_edge;
813         last_track_bottom_edge = 0;
814         for (std::vector<TimeAxisView*>::reverse_iterator t = _time_axis_views.rbegin(); t != _time_axis_views.rend(); ++t) {
815                 if (!(*t)->hidden()) {
816                         last_track_bottom_edge = (*t)->canvas_display()->canvas_origin ().y + (*t)->effective_height();
817                         break;
818                 }
819         }
820
821         if (tv && tv->view()) {
822                 /* the mouse is over a track */
823                 double layer = r.second;
824
825                 if (first_move && tv->view()->layer_display() == Stacked) {
826                         tv->view()->set_layer_display (Expanded);
827                 }
828
829                 /* Here's the current pointer position in terms of time axis view and layer */
830                 current_pointer_time_axis_view = find_time_axis_view (tv);
831                 assert(current_pointer_time_axis_view >= 0);
832
833                 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
834
835                 /* Work out the change in y */
836
837                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
838                 if (!rtv || !rtv->is_track()) {
839                         /* ignore busses early on. we can't move any regions on them */
840                 } else if (_last_pointer_time_axis_view < 0) {
841                         /* Was in the drop-zone, now over a track.
842                          * Hence it must be an upward move (from the bottom)
843                          *
844                          * track_index is still -1, so delta must be set to
845                          * move up the correct number of tracks from the bottom.
846                          *
847                          * This is necessary because steps may be skipped if
848                          * the bottom-most track is not a valid target and/or
849                          * if there are hidden tracks at the bottom.
850                          * Hence the initial offset (_ddropzone) as well as the
851                          * last valid pointer position (_pdropzone) need to be
852                          * taken into account.
853                          */
854                         delta_time_axis_view = current_pointer_time_axis_view - _time_axis_views.size () + _ddropzone - _pdropzone;
855                 } else {
856                         delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
857                 }
858
859                 /* TODO needs adjustment per DraggingView,
860                  *
861                  * e.g. select one region on the top-layer of a track
862                  * and one region which is at the bottom-layer of another track
863                  * drag both.
864                  *
865                  * Indicated drop-zones and layering is wrong.
866                  * and may infer additional layers on the target-track
867                  * (depending how many layers the original track had).
868                  *
869                  * Or select two regions (different layers) on a same track,
870                  * move across a non-layer track.. -> layering info is lost.
871                  * on drop either of the regions may be on top.
872                  *
873                  * Proposed solution: screw it :) well,
874                  *   don't use delta_layer, use an absolute value
875                  *   1) remember DraggingView's layer  as float 0..1  [current layer / all layers of source]
876                  *   2) calculate current mouse pos, y-pos inside track divided by height of mouse-over track.
877                  *   3) iterate over all DraggingView, find the one that is over the track with most layers
878                  *   4) proportionally scale layer to layers available on target
879                  */
880                 delta_layer = current_pointer_layer - _last_pointer_layer;
881
882         }
883         /* for automation lanes, there is a TimeAxisView but no ->view()
884          * if (!tv) -> dropzone 
885          */
886         else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view >= 0) {
887                 /* Moving into the drop-zone.. */
888                 delta_time_axis_view = _time_axis_views.size () - _last_pointer_time_axis_view;
889                 /* delta_time_axis_view may not be sufficient to move into the DZ
890                  * the mouse may enter it, but it may not be a valid move due to
891                  * constraints.
892                  *
893                  * -> remember the delta needed to move into the dropzone
894                  */
895                 _ddropzone = delta_time_axis_view;
896                 /* ..but subtract hidden tracks (or routes) at the bottom.
897                  * we silently move mover them
898                  */
899                 _ddropzone -= apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
900                               - _time_axis_views.size();
901         }
902         else if (!tv && cur_y >= 0 && _last_pointer_time_axis_view < 0) {
903                 /* move around inside the zone.
904                  * This allows to move further down until all regions are in the zone.
905                  */
906                 const double ptr_y = cur_y + _editor->get_trackview_group()->canvas_origin().y;
907                 assert(ptr_y >= last_track_bottom_edge);
908                 assert(_ddropzone > 0);
909
910                 /* calculate mouse position in 'tracks' below last track. */
911                 const double dzi_h = TimeAxisView::preset_height (HeightNormal);
912                 uint32_t dzpos = _ddropzone + floor((1 + ptr_y - last_track_bottom_edge) / dzi_h);
913
914                 if (dzpos > _pdropzone && _ndropzone < _ntracks) {
915                         // move further down
916                         delta_time_axis_view =  dzpos - _pdropzone;
917                 } else if (dzpos < _pdropzone && _ndropzone > 0) {
918                         // move up inside the DZ
919                         delta_time_axis_view =  dzpos - _pdropzone;
920                 }
921         }
922
923         /* Work out the change in x */
924         framepos_t pending_region_position;
925         double const x_delta = compute_x_delta (event, &pending_region_position);
926         _last_frame_position = pending_region_position;
927
928         /* calculate hidden tracks in current y-axis delta */
929         int delta_skip = 0;
930         if (_last_pointer_time_axis_view < 0 && _pdropzone > 0) {
931                 /* The mouse is more than one track below the dropzone.
932                  * distance calculation is not needed (and would not work, either
933                  * because the dropzone is "packed").
934                  *
935                  * Except when [partially] moving regions out of dropzone in a large step.
936                  * (the mouse may or may not remain in the DZ)
937                  * Hidden tracks at the bottom of the TAV need to be skipped.
938                  *
939                  * This also handles the case if the mouse entered the DZ
940                  * in a large step (exessive delta), either due to fast-movement,
941                  * autoscroll, laggy UI. _ddropzone copensates for that (see "move into dz" above)
942                  */
943                 if (delta_time_axis_view < 0 && (int)_ddropzone - delta_time_axis_view >= (int)_pdropzone) {
944                         const int dt = delta_time_axis_view + (int)_pdropzone - (int)_ddropzone;
945                         assert(dt <= 0);
946                         delta_skip = apply_track_delta(_time_axis_views.size(), dt, 0, true)
947                                 -_time_axis_views.size() - dt;
948                 }
949         }
950         else if (_last_pointer_time_axis_view < 0) {
951                 /* Moving out of the zone. Check for hidden tracks at the bottom. */
952                 delta_skip = apply_track_delta(_time_axis_views.size(), delta_time_axis_view, 0, true)
953                              -_time_axis_views.size() - delta_time_axis_view;
954         } else {
955                 /* calculate hidden tracks that are skipped by the pointer movement */
956                 delta_skip = apply_track_delta(_last_pointer_time_axis_view, delta_time_axis_view, 0, true)
957                              - _last_pointer_time_axis_view
958                              - delta_time_axis_view;
959         }
960
961         /* Verify change in y */
962         if (!y_movement_allowed (delta_time_axis_view, delta_layer, delta_skip)) {
963                 /* this y movement is not allowed, so do no y movement this time */
964                 delta_time_axis_view = 0;
965                 delta_layer = 0;
966                 delta_skip = 0;
967         }
968
969         if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
970                 /* haven't reached next snap point, and we're not switching
971                    trackviews nor layers. nothing to do.
972                 */
973                 return;
974         }
975
976         typedef map<boost::shared_ptr<Playlist>, double> PlaylistDropzoneMap;
977         PlaylistDropzoneMap playlist_dropzone_map;
978         _ndropzone = 0; // number of elements currently in the dropzone
979
980         if (first_move) {
981                 /* sort views by time_axis.
982                  * This retains track order in the dropzone, regardless
983                  * of actual selection order
984                  */
985                 _views.sort (DraggingViewSorter());
986
987                 /* count number of distinct tracks of all regions
988                  * being dragged, used for dropzone.
989                  */
990                 int prev_track = -1;
991                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
992                         if (i->time_axis_view != prev_track) {
993                                 prev_track = i->time_axis_view;
994                                 ++_ntracks;
995                         }
996                 }
997 #ifndef NDEBUG
998                 int spread =
999                         _views.back().time_axis_view -
1000                         _views.front().time_axis_view;
1001
1002                 spread -= apply_track_delta (_views.front().time_axis_view, spread, 0, true)
1003                           -  _views.back().time_axis_view;
1004
1005                 printf("Dragging region(s) from %d different track(s), max dist: %d\n", _ntracks, spread);
1006 #endif
1007         }
1008
1009         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1010
1011                 RegionView* rv = i->view;
1012                 double y_delta;
1013
1014                 y_delta = 0;
1015
1016                 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1017                         continue;
1018                 }
1019
1020                 if (first_move) {
1021                         rv->drag_start ();
1022
1023                         /* reparent the regionview into a group above all
1024                          * others
1025                          */
1026
1027                         ArdourCanvas::Item* rvg = rv->get_canvas_group();
1028                         Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
1029                         Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1030                         rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
1031                         /* move the item so that it continues to appear at the
1032                            same location now that its parent has changed.
1033                            */
1034                         rvg->move (rv_canvas_offset - dmg_canvas_offset);
1035                 }
1036
1037                 /* If we have moved tracks, we'll fudge the layer delta so that the
1038                    region gets moved back onto layer 0 on its new track; this avoids
1039                    confusion when dragging regions from non-zero layers onto different
1040                    tracks.
1041                 */
1042                 double this_delta_layer = delta_layer;
1043                 if (delta_time_axis_view != 0) {
1044                         this_delta_layer = - i->layer;
1045                 }
1046
1047                 int this_delta_time_axis_view = apply_track_delta(i->time_axis_view, delta_time_axis_view, delta_skip) - i->time_axis_view;
1048
1049                 int track_index = i->time_axis_view + this_delta_time_axis_view;
1050                 assert(track_index >= 0);
1051
1052                 if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
1053                         /* Track is in the Dropzone */
1054
1055                         i->time_axis_view = track_index;
1056                         assert(i->time_axis_view >= (int) _time_axis_views.size());
1057                         if (cur_y >= 0) {
1058
1059                                 double yposition = 0;
1060                                 PlaylistDropzoneMap::iterator pdz = playlist_dropzone_map.find (i->view->region()->playlist());
1061                                 rv->set_height (TimeAxisView::preset_height (HeightNormal));
1062                                 ++_ndropzone;
1063
1064                                 /* store index of each new playlist as a negative count, starting at -1 */
1065
1066                                 if (pdz == playlist_dropzone_map.end()) {
1067                                         /* compute where this new track (which doesn't exist yet) will live
1068                                            on the y-axis.
1069                                         */
1070                                         yposition = last_track_bottom_edge; /* where to place the top edge of the regionview */
1071
1072                                         /* How high is this region view ? */
1073
1074                                         boost::optional<ArdourCanvas::Rect> obbox = rv->get_canvas_group()->bounding_box ();
1075                                         ArdourCanvas::Rect bbox;
1076
1077                                         if (obbox) {
1078                                                 bbox = obbox.get ();
1079                                         }
1080
1081                                         last_track_bottom_edge += bbox.height();
1082
1083                                         playlist_dropzone_map.insert (make_pair (i->view->region()->playlist(), yposition));
1084
1085                                 } else {
1086                                         yposition = pdz->second;
1087                                 }
1088
1089                                 /* values are zero or negative, hence the use of min() */
1090                                 y_delta = yposition - rv->get_canvas_group()->canvas_origin().y;
1091                         }
1092
1093                 } else {
1094
1095                         /* The TimeAxisView that this region is now over */
1096                         TimeAxisView* current_tv = _time_axis_views[track_index];
1097
1098                         /* Ensure it is moved from stacked -> expanded if appropriate */
1099                         if (current_tv->view()->layer_display() == Stacked) {
1100                                 current_tv->view()->set_layer_display (Expanded);
1101                         }
1102
1103                         /* We're only allowed to go -ve in layer on Expanded views */
1104                         if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
1105                                 this_delta_layer = - i->layer;
1106                         }
1107
1108                         /* Set height */
1109                         rv->set_height (current_tv->view()->child_height ());
1110
1111                         /* Update show/hidden status as the region view may have come from a hidden track,
1112                            or have moved to one.
1113                         */
1114                         if (current_tv->hidden ()) {
1115                                 rv->get_canvas_group()->hide ();
1116                         } else {
1117                                 rv->get_canvas_group()->show ();
1118                         }
1119
1120                         /* Update the DraggingView */
1121                         i->time_axis_view = track_index;
1122                         i->layer += this_delta_layer;
1123
1124                         if (_brushing) {
1125                                 _editor->mouse_brush_insert_region (rv, pending_region_position);
1126                         } else {
1127                                 Duple track_origin;
1128
1129                                 /* Get the y coordinate of the top of the track that this region is now over */
1130                                 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
1131
1132                                 /* And adjust for the layer that it should be on */
1133                                 StreamView* cv = current_tv->view ();
1134                                 switch (cv->layer_display ()) {
1135                                 case Overlaid:
1136                                         break;
1137                                 case Stacked:
1138                                         track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
1139                                         break;
1140                                 case Expanded:
1141                                         track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
1142                                         break;
1143                                 }
1144
1145                                 /* need to get the parent of the regionview
1146                                  * canvas group and get its position in
1147                                  * equivalent coordinate space as the trackview
1148                                  * we are now dragging over.
1149                                  */
1150
1151                                 y_delta = track_origin.y - rv->get_canvas_group()->canvas_origin().y;
1152
1153                         }
1154                 }
1155
1156                 /* Now move the region view */
1157                 rv->move (x_delta, y_delta);
1158
1159         } /* foreach region */
1160
1161         _total_x_delta += x_delta;
1162
1163         if (x_delta != 0 && !_brushing) {
1164                 show_verbose_cursor_time (_last_frame_position);
1165         }
1166
1167         /* keep track of pointer movement */
1168         if (tv) {
1169                 /* the pointer is currently over a time axis view */
1170
1171                 if (_last_pointer_time_axis_view < 0) {
1172                         /* last motion event was not over a time axis view
1173                          * or last y-movement out of the dropzone was not valid
1174                          */
1175                         int dtz = 0;
1176                         if (delta_time_axis_view < 0) {
1177                                 /* in the drop zone, moving up */
1178
1179                                 /* _pdropzone is the last known pointer y-axis position inside the DZ.
1180                                  * We do not use negative _last_pointer_time_axis_view because
1181                                  * the dropzone is "packed" (the actual track offset is ignored)
1182                                  *
1183                                  * As opposed to the actual number 
1184                                  * of elements in the dropzone (_ndropzone)
1185                                  * _pdropzone is not constrained. This is necessary
1186                                  * to allow moving multiple regions with y-distance
1187                                  * into the DZ.
1188                                  *
1189                                  * There can be 0 elements in the dropzone,
1190                                  * even though the drag-pointer is inside the DZ.
1191                                  *
1192                                  * example:
1193                                  * [ Audio-track, Midi-track, Audio-track, DZ ]
1194                                  * move regions from both audio tracks at the same time into the
1195                                  * DZ by grabbing the region in the bottom track.
1196                                  */
1197                                 assert(current_pointer_time_axis_view >= 0);
1198                                 dtz = std::min((int)_pdropzone, (int)_ddropzone - delta_time_axis_view);
1199                                 _pdropzone -= dtz;
1200                         }
1201
1202                         /* only move out of the zone if the movement is OK */
1203                         if (_pdropzone == 0 && delta_time_axis_view != 0) {
1204                                 assert(delta_time_axis_view < 0);
1205                                 _last_pointer_time_axis_view = current_pointer_time_axis_view;
1206                                 /* if all logic and maths are correct, there is no need to assign the 'current' pointer.
1207                                  * the current position can be calculated as follows:
1208                                  */
1209                                 // a well placed oofus attack can still throw this off.
1210                                 // likley auto-scroll related, printf() debugging may tell, commented out for now.
1211                                 //assert (current_pointer_time_axis_view == _time_axis_views.size() - dtz + _ddropzone + delta_time_axis_view);
1212                         }
1213                 } else {
1214                         /* last motion event was also over a time axis view */
1215                         _last_pointer_time_axis_view += delta_time_axis_view;
1216                         assert(_last_pointer_time_axis_view >= 0);
1217                 }
1218
1219         } else {
1220
1221                 /* the pointer is not over a time axis view */
1222                 assert ((delta_time_axis_view > 0) || (((int)_pdropzone) >= (delta_skip - delta_time_axis_view)));
1223                 _pdropzone += delta_time_axis_view - delta_skip;
1224                 _last_pointer_time_axis_view = -1; // <0 : we're in the zone, value does not matter.
1225         }
1226
1227         _last_pointer_layer += delta_layer;
1228 }
1229
1230 void
1231 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
1232 {
1233         if (_copy && first_move) {
1234                 if (_x_constrained && !_brushing) {
1235                         _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1236                 } else if (!_brushing) {
1237                         _editor->begin_reversible_command (Operations::region_copy);
1238                 } else if (_brushing) {
1239                         _editor->begin_reversible_command (Operations::drag_region_brush);
1240                 }
1241                 /* duplicate the regionview(s) and region(s) */
1242
1243                 list<DraggingView> new_regionviews;
1244
1245                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1246
1247                         RegionView* rv = i->view;
1248                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1249                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1250
1251                         const boost::shared_ptr<const Region> original = rv->region();
1252                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
1253                         region_copy->set_position (original->position());
1254                         /* need to set this so that the drop zone code can work. This doesn't
1255                            actually put the region into the playlist, but just sets a weak pointer
1256                            to it.
1257                         */
1258                         region_copy->set_playlist (original->playlist());
1259
1260                         RegionView* nrv;
1261                         if (arv) {
1262                                 boost::shared_ptr<AudioRegion> audioregion_copy
1263                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1264
1265                                 nrv = new AudioRegionView (*arv, audioregion_copy);
1266                         } else if (mrv) {
1267                                 boost::shared_ptr<MidiRegion> midiregion_copy
1268                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1269                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
1270                         } else {
1271                                 continue;
1272                         }
1273
1274                         nrv->get_canvas_group()->show ();
1275                         new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
1276
1277                         /* swap _primary to the copy */
1278
1279                         if (rv == _primary) {
1280                                 _primary = nrv;
1281                         }
1282
1283                         /* ..and deselect the one we copied */
1284
1285                         rv->set_selected (false);
1286                 }
1287
1288                 if (!new_regionviews.empty()) {
1289
1290                         /* reflect the fact that we are dragging the copies */
1291
1292                         _views = new_regionviews;
1293
1294                         swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
1295                 }
1296
1297         } else if (!_copy && first_move) {
1298                 if (_x_constrained && !_brushing) {
1299                         _editor->begin_reversible_command (_("fixed time region drag"));
1300                 } else if (!_brushing) {
1301                         _editor->begin_reversible_command (Operations::region_drag);
1302                 } else if (_brushing) {
1303                         _editor->begin_reversible_command (Operations::drag_region_brush);
1304                 }
1305         }
1306         RegionMotionDrag::motion (event, first_move);
1307 }
1308
1309 void
1310 RegionMotionDrag::finished (GdkEvent *, bool)
1311 {
1312         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1313                 if (!(*i)->view()) {
1314                         continue;
1315                 }
1316
1317                 if ((*i)->view()->layer_display() == Expanded) {
1318                         (*i)->view()->set_layer_display (Stacked);
1319                 }
1320         }
1321 }
1322
1323 void
1324 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
1325 {
1326         RegionMotionDrag::finished (ev, movement_occurred);
1327
1328         if (!movement_occurred) {
1329
1330                 /* just a click */
1331
1332                 if (was_double_click() && !_views.empty()) {
1333                         DraggingView dv = _views.front();
1334                         dv.view->show_region_editor ();
1335
1336                 }
1337
1338                 return;
1339         }
1340
1341         assert (!_views.empty ());
1342
1343         /* We might have hidden region views so that they weren't visible during the drag
1344            (when they have been reparented).  Now everything can be shown again, as region
1345            views are back in their track parent groups.
1346         */
1347         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1348                 i->view->get_canvas_group()->show ();
1349         }
1350
1351         bool const changed_position = (_last_frame_position != _primary->region()->position());
1352         bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
1353         framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
1354
1355         if (_copy) {
1356
1357                 finished_copy (
1358                         changed_position,
1359                         changed_tracks,
1360                         drag_delta
1361                         );
1362
1363         } else {
1364
1365                 finished_no_copy (
1366                         changed_position,
1367                         changed_tracks,
1368                         drag_delta
1369                         );
1370
1371         }
1372
1373         _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
1374 }
1375
1376 RouteTimeAxisView*
1377 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1378 {
1379         /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1380            new track.
1381          */
1382
1383         try {
1384                 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1385                         list<boost::shared_ptr<AudioTrack> > audio_tracks;
1386                         uint32_t output_chan = region->n_channels();
1387                         if ((Config->get_output_auto_connect() & AutoConnectMaster) && _editor->session()->master_out()) {
1388                                 output_chan =  _editor->session()->master_out()->n_inputs().n_audio();
1389                         }
1390                         audio_tracks = _editor->session()->new_audio_track (region->n_channels(), output_chan, ARDOUR::Normal, 0, 1, region->name());
1391                         RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1392                         if (rtav) {
1393                                 rtav->set_height (original->current_height());
1394                         }
1395                         return rtav;
1396                 } else {
1397                         ChanCount one_midi_port (DataType::MIDI, 1);
1398                         list<boost::shared_ptr<MidiTrack> > midi_tracks;
1399                         midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1400                         RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1401                         if (rtav) {
1402                                 rtav->set_height (original->current_height());
1403                         }
1404                         return rtav;
1405                 }
1406         } catch (...) {
1407                 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1408                 return 0;
1409         }
1410 }
1411
1412 void
1413 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1414 {
1415         RegionSelection new_views;
1416         PlaylistSet modified_playlists;
1417         RouteTimeAxisView* new_time_axis_view = 0;
1418
1419         if (_brushing) {
1420                 /* all changes were made during motion event handlers */
1421
1422                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1423                         delete i->view;
1424                 }
1425
1426                 _editor->commit_reversible_command ();
1427                 return;
1428         }
1429
1430         typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1431         PlaylistMapping playlist_mapping;
1432
1433         /* insert the regions into their new playlists */
1434         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1435
1436                 RouteTimeAxisView* dest_rtv = 0;
1437
1438                 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1439                         continue;
1440                 }
1441
1442                 framepos_t where;
1443
1444                 if (changed_position && !_x_constrained) {
1445                         where = i->view->region()->position() - drag_delta;
1446                 } else {
1447                         where = i->view->region()->position();
1448                 }
1449
1450                 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1451                         /* dragged to drop zone */
1452
1453                         PlaylistMapping::iterator pm;
1454
1455                         if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1456                                 /* first region from this original playlist: create a new track */
1457                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1458                                 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1459                                 dest_rtv = new_time_axis_view;
1460                         } else {
1461                                 /* we already created a new track for regions from this playlist, use it */
1462                                 dest_rtv = pm->second;
1463                         }
1464                 } else {
1465                         /* destination time axis view is the one we dragged to */
1466                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1467                 }
1468
1469                 if (dest_rtv != 0) {
1470                         RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1471                         if (new_view != 0) {
1472                                 new_views.push_back (new_view);
1473                         }
1474                 }
1475
1476                 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1477                    since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1478                  */
1479
1480                 list<DraggingView>::const_iterator next = i;
1481                 ++next;
1482                 delete i->view;
1483                 i = next;
1484         }
1485
1486         /* If we've created new regions either by copying or moving
1487            to a new track, we want to replace the old selection with the new ones
1488         */
1489
1490         if (new_views.size() > 0) {
1491                 _editor->selection->set (new_views);
1492         }
1493
1494         /* write commands for the accumulated diffs for all our modified playlists */
1495         add_stateful_diff_commands_for_playlists (modified_playlists);
1496
1497         _editor->commit_reversible_command ();
1498 }
1499
1500 void
1501 RegionMoveDrag::finished_no_copy (
1502         bool const changed_position,
1503         bool const changed_tracks,
1504         framecnt_t const drag_delta
1505         )
1506 {
1507         RegionSelection new_views;
1508         PlaylistSet modified_playlists;
1509         PlaylistSet frozen_playlists;
1510         set<RouteTimeAxisView*> views_to_update;
1511         RouteTimeAxisView* new_time_axis_view = 0;
1512
1513         typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1514         PlaylistMapping playlist_mapping;
1515
1516         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1517
1518                 RegionView* rv = i->view;
1519                 RouteTimeAxisView* dest_rtv = 0;
1520
1521                 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1522                         ++i;
1523                         continue;
1524                 }
1525
1526                 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1527                         /* dragged to drop zone */
1528
1529                         PlaylistMapping::iterator pm;
1530
1531                         if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1532                                 /* first region from this original playlist: create a new track */
1533                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1534                                 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1535                                 dest_rtv = new_time_axis_view;
1536                         } else {
1537                                 /* we already created a new track for regions from this playlist, use it */
1538                                 dest_rtv = pm->second;
1539                         }
1540
1541                 } else {
1542                         /* destination time axis view is the one we dragged to */
1543                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1544                 }
1545
1546                 assert (dest_rtv);
1547
1548                 double const dest_layer = i->layer;
1549
1550                 views_to_update.insert (dest_rtv);
1551
1552                 framepos_t where;
1553
1554                 if (changed_position && !_x_constrained) {
1555                         where = rv->region()->position() - drag_delta;
1556                 } else {
1557                         where = rv->region()->position();
1558                 }
1559
1560                 if (changed_tracks) {
1561
1562                         /* insert into new playlist */
1563
1564                         RegionView* new_view = insert_region_into_playlist (
1565                                 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1566                                 );
1567
1568                         if (new_view == 0) {
1569                                 ++i;
1570                                 continue;
1571                         }
1572
1573                         new_views.push_back (new_view);
1574
1575                         /* remove from old playlist */
1576
1577                         /* the region that used to be in the old playlist is not
1578                            moved to the new one - we use a copy of it. as a result,
1579                            any existing editor for the region should no longer be
1580                            visible.
1581                         */
1582                         rv->hide_region_editor();
1583
1584
1585                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1586
1587                 } else {
1588
1589                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1590
1591                         /* this movement may result in a crossfade being modified, or a layering change,
1592                            so we need to get undo data from the playlist as well as the region.
1593                         */
1594
1595                         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1596                         if (r.second) {
1597                                 playlist->clear_changes ();
1598                         }
1599
1600                         rv->region()->clear_changes ();
1601
1602                         /*
1603                            motion on the same track. plonk the previously reparented region
1604                            back to its original canvas group (its streamview).
1605                            No need to do anything for copies as they are fake regions which will be deleted.
1606                         */
1607
1608                         rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1609                         rv->get_canvas_group()->set_y_position (i->initial_y);
1610                         rv->drag_end ();
1611
1612                         /* just change the model */
1613                         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1614                                 playlist->set_layer (rv->region(), dest_layer);
1615                         }
1616
1617                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1618
1619                         r = frozen_playlists.insert (playlist);
1620
1621                         if (r.second) {
1622                                 playlist->freeze ();
1623                         }
1624
1625                         rv->region()->set_position (where);
1626                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1627                 }
1628
1629                 if (changed_tracks) {
1630
1631                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1632                            was selected in all of them, then removing it from a playlist will have removed all
1633                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1634                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1635                            corresponding regionview, and _views is now empty).
1636
1637                            This could have invalidated any and all iterators into _views.
1638
1639                            The heuristic we use here is: if the region selection is empty, break out of the loop
1640                            here. if the region selection is not empty, then restart the loop because we know that
1641                            we must have removed at least the region(view) we've just been working on as well as any
1642                            that we processed on previous iterations.
1643
1644                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1645                            we can just iterate.
1646                         */
1647
1648
1649                         if (_views.empty()) {
1650                                 break;
1651                         } else {
1652                                 i = _views.begin();
1653                         }
1654
1655                 } else {
1656                         ++i;
1657                 }
1658         }
1659
1660         /* If we've created new regions either by copying or moving
1661            to a new track, we want to replace the old selection with the new ones
1662         */
1663
1664         if (new_views.size() > 0) {
1665                 _editor->selection->set (new_views);
1666         }
1667
1668         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1669                 (*p)->thaw();
1670         }
1671
1672         /* write commands for the accumulated diffs for all our modified playlists */
1673         add_stateful_diff_commands_for_playlists (modified_playlists);
1674         /* applies to _brushing */
1675         _editor->commit_reversible_command ();
1676
1677         /* We have futzed with the layering of canvas items on our streamviews.
1678            If any region changed layer, this will have resulted in the stream
1679            views being asked to set up their region views, and all will be well.
1680            If not, we might now have badly-ordered region views.  Ask the StreamViews
1681            involved to sort themselves out, just in case.
1682         */
1683
1684         for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1685                 (*i)->view()->playlist_layered ((*i)->track ());
1686         }
1687 }
1688
1689 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1690  *  @param region Region to remove.
1691  *  @param playlist playlist To remove from.
1692  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1693  *  that clear_changes () is only called once per playlist.
1694  */
1695 void
1696 RegionMoveDrag::remove_region_from_playlist (
1697         boost::shared_ptr<Region> region,
1698         boost::shared_ptr<Playlist> playlist,
1699         PlaylistSet& modified_playlists
1700         )
1701 {
1702         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1703
1704         if (r.second) {
1705                 playlist->clear_changes ();
1706         }
1707
1708         playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1709 }
1710
1711
1712 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1713  *  clearing the playlist's diff history first if necessary.
1714  *  @param region Region to insert.
1715  *  @param dest_rtv Destination RouteTimeAxisView.
1716  *  @param dest_layer Destination layer.
1717  *  @param where Destination position.
1718  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1719  *  that clear_changes () is only called once per playlist.
1720  *  @return New RegionView, or 0 if no insert was performed.
1721  */
1722 RegionView *
1723 RegionMoveDrag::insert_region_into_playlist (
1724         boost::shared_ptr<Region> region,
1725         RouteTimeAxisView* dest_rtv,
1726         layer_t dest_layer,
1727         framecnt_t where,
1728         PlaylistSet& modified_playlists
1729         )
1730 {
1731         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1732         if (!dest_playlist) {
1733                 return 0;
1734         }
1735
1736         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1737         _new_region_view = 0;
1738         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1739
1740         /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1741         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1742         if (r.second) {
1743                 dest_playlist->clear_changes ();
1744         }
1745
1746         dest_playlist->add_region (region, where);
1747
1748         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1749                 dest_playlist->set_layer (region, dest_layer);
1750         }
1751
1752         c.disconnect ();
1753
1754         assert (_new_region_view);
1755
1756         return _new_region_view;
1757 }
1758
1759 void
1760 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1761 {
1762         _new_region_view = rv;
1763 }
1764
1765 void
1766 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1767 {
1768         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1769                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1770                 if (!c->empty()) {
1771                         _editor->session()->add_command (c);
1772                 } else {
1773                         delete c;
1774                 }
1775         }
1776 }
1777
1778
1779 void
1780 RegionMoveDrag::aborted (bool movement_occurred)
1781 {
1782         if (_copy) {
1783
1784                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1785                         list<DraggingView>::const_iterator next = i;
1786                         ++next;
1787                         delete i->view;
1788                         i = next;
1789                 }
1790
1791                 _views.clear ();
1792
1793         } else {
1794                 RegionMotionDrag::aborted (movement_occurred);
1795         }
1796 }
1797
1798 void
1799 RegionMotionDrag::aborted (bool)
1800 {
1801         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1802
1803                 StreamView* sview = (*i)->view();
1804
1805                 if (sview) {
1806                         if (sview->layer_display() == Expanded) {
1807                                 sview->set_layer_display (Stacked);
1808                         }
1809                 }
1810         }
1811
1812         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1813                 RegionView* rv = i->view;
1814                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1815                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1816                 assert (rtv);
1817                 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1818                 rv->get_canvas_group()->set_y_position (0);
1819                 rv->drag_end ();
1820                 rv->move (-_total_x_delta, 0);
1821                 rv->set_height (rtv->view()->child_height ());
1822         }
1823 }
1824
1825 /** @param b true to brush, otherwise false.
1826  *  @param c true to make copies of the regions being moved, otherwise false.
1827  */
1828 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1829         : RegionMotionDrag (e, i, p, v, b)
1830         , _copy (c)
1831 {
1832         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1833
1834         double speed = 1;
1835         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1836         if (rtv && rtv->is_track()) {
1837                 speed = rtv->track()->speed ();
1838         }
1839
1840         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1841 }
1842
1843 void
1844 RegionMoveDrag::setup_pointer_frame_offset ()
1845 {
1846         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1847 }
1848
1849 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1850         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1851 {
1852         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1853
1854         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1855                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1856
1857         _primary = v->view()->create_region_view (r, false, false);
1858
1859         _primary->get_canvas_group()->show ();
1860         _primary->set_position (pos, 0);
1861         _views.push_back (DraggingView (_primary, this, v));
1862
1863         _last_frame_position = pos;
1864
1865         _item = _primary->get_canvas_group ();
1866 }
1867
1868 void
1869 RegionInsertDrag::finished (GdkEvent *, bool)
1870 {
1871         int pos = _views.front().time_axis_view;
1872         assert(pos >= 0 && pos < (int)_time_axis_views.size());
1873
1874         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1875
1876         _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1877         _primary->get_canvas_group()->set_y_position (0);
1878
1879         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1880
1881         _editor->begin_reversible_command (Operations::insert_region);
1882         playlist->clear_changes ();
1883         playlist->add_region (_primary->region (), _last_frame_position);
1884
1885         // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1886         if (Config->get_edit_mode() == Ripple) {
1887                 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1888         }
1889
1890         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1891         _editor->commit_reversible_command ();
1892
1893         delete _primary;
1894         _primary = 0;
1895         _views.clear ();
1896 }
1897
1898 void
1899 RegionInsertDrag::aborted (bool)
1900 {
1901         delete _primary;
1902         _primary = 0;
1903         _views.clear ();
1904 }
1905
1906 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1907         : RegionMoveDrag (e, i, p, v, false, false)
1908 {
1909         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1910 }
1911
1912 struct RegionSelectionByPosition {
1913         bool operator() (RegionView*a, RegionView* b) {
1914                 return a->region()->position () < b->region()->position();
1915         }
1916 };
1917
1918 void
1919 RegionSpliceDrag::motion (GdkEvent* event, bool)
1920 {
1921         /* Which trackview is this ? */
1922
1923         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1924         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1925
1926         /* The region motion is only processed if the pointer is over
1927            an audio track.
1928         */
1929
1930         if (!tv || !tv->is_track()) {
1931                 /* To make sure we hide the verbose canvas cursor when the mouse is
1932                    not held over an audio track.
1933                 */
1934                 _editor->verbose_cursor()->hide ();
1935                 return;
1936         } else {
1937                 _editor->verbose_cursor()->show ();
1938         }
1939
1940         int dir;
1941
1942         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1943                 dir = 1;
1944         } else {
1945                 dir = -1;
1946         }
1947
1948         RegionSelection copy;
1949         _editor->selection->regions.by_position(copy);
1950
1951         framepos_t const pf = adjusted_current_frame (event);
1952
1953         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1954
1955                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1956
1957                 if (!atv) {
1958                         continue;
1959                 }
1960
1961                 boost::shared_ptr<Playlist> playlist;
1962
1963                 if ((playlist = atv->playlist()) == 0) {
1964                         continue;
1965                 }
1966
1967                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1968                         continue;
1969                 }
1970
1971                 if (dir > 0) {
1972                         if (pf < (*i)->region()->last_frame() + 1) {
1973                                 continue;
1974                         }
1975                 } else {
1976                         if (pf > (*i)->region()->first_frame()) {
1977                                 continue;
1978                         }
1979                 }
1980
1981
1982                 playlist->shuffle ((*i)->region(), dir);
1983         }
1984 }
1985
1986 void
1987 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1988 {
1989         RegionMoveDrag::finished (event, movement_occurred);
1990 }
1991
1992 void
1993 RegionSpliceDrag::aborted (bool)
1994 {
1995         /* XXX: TODO */
1996 }
1997
1998 /***
1999  * ripple mode...
2000  */
2001
2002 void
2003 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2004 {
2005
2006         boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2007
2008         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2009         RegionSelection to_ripple;
2010         for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2011                 if ((*i)->position() >= where) {
2012                         to_ripple.push_back (rtv->view()->find_view(*i));
2013                 }
2014         }
2015
2016         for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2017                 if (!exclude.contains (*i)) {
2018                         // the selection has already been added to _views
2019
2020                         if (drag_in_progress) {
2021                                 // do the same things that RegionMotionDrag::motion does when
2022                                 // first_move is true, for the region views that we're adding
2023                                 // to _views this time
2024
2025                                 (*i)->drag_start();
2026                                 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2027                                 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2028                                 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2029                                 rvg->reparent (_editor->_drag_motion_group);
2030
2031                                 // we only need to move in the y direction
2032                                 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2033                                 fudge.x = 0;
2034                                 rvg->move (fudge);
2035
2036                         }
2037                         _views.push_back (DraggingView (*i, this, tav));
2038                 }
2039         }
2040 }
2041
2042 void
2043 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2044 {
2045
2046         for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2047                 // we added all the regions after the selection
2048
2049                 std::list<DraggingView>::iterator to_erase = i++;
2050                 if (!_editor->selection->regions.contains (to_erase->view)) {
2051                         // restore the non-selected regions to their original playlist & positions,
2052                         // and then ripple them back by the length of the regions that were dragged away
2053                         // do the same things as RegionMotionDrag::aborted
2054
2055                         RegionView *rv = to_erase->view;
2056                         TimeAxisView* tv = &(rv->get_time_axis_view ());
2057                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2058                         assert (rtv);
2059
2060                         // plonk them back onto their own track
2061                         rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2062                         rv->get_canvas_group()->set_y_position (0);
2063                         rv->drag_end ();
2064
2065                         if (move_regions) {
2066                                 // move the underlying region to match the view
2067                                 rv->region()->set_position (rv->region()->position() + amount);
2068                         } else {
2069                                 // restore the view to match the underlying region's original position
2070                                 rv->move(-amount, 0);   // second parameter is y delta - seems 0 is OK
2071                         }
2072
2073                         rv->set_height (rtv->view()->child_height ());
2074                         _views.erase (to_erase);
2075                 }
2076         }
2077 }
2078
2079 bool
2080 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2081 {
2082         if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2083                 if (delta_track) {
2084                         return allow_moves_across_tracks;
2085                 } else {
2086                         return true;
2087                 }
2088         }
2089         return false;
2090 }
2091
2092 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2093         : RegionMoveDrag (e, i, p, v, false, false)
2094 {
2095         DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2096         // compute length of selection
2097         RegionSelection selected_regions = _editor->selection->regions;
2098         selection_length = selected_regions.end_frame() - selected_regions.start();
2099
2100         // we'll only allow dragging to another track in ripple mode if all the regions
2101         // being dragged start off on the same track
2102         allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2103         prev_tav = NULL;
2104         prev_amount = 0;
2105         exclude = new RegionList;
2106         for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2107                 exclude->push_back((*i)->region());
2108         }
2109
2110         // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2111         RegionSelection copy;
2112         selected_regions.by_position(copy); // get selected regions sorted by position into copy
2113
2114         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2115         std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2116
2117         for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2118                 // find ripple start point on each applicable playlist
2119                 RegionView *first_selected_on_this_track = NULL;
2120                 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2121                         if ((*i)->region()->playlist() == (*pi)) {
2122                                 // region is on this playlist - it's the first, because they're sorted
2123                                 first_selected_on_this_track = *i;
2124                                 break;
2125                         }
2126                 }
2127                 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2128                 add_all_after_to_views (
2129                                 &first_selected_on_this_track->get_time_axis_view(),
2130                                 first_selected_on_this_track->region()->position(),
2131                                 selected_regions, false);
2132         }
2133
2134         if (allow_moves_across_tracks) {
2135                 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2136         } else {
2137                 orig_tav = NULL;
2138         }
2139
2140 }
2141
2142 void
2143 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2144 {
2145         /* Which trackview is this ? */
2146
2147         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2148         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2149
2150         /* The region motion is only processed if the pointer is over
2151            an audio track.
2152          */
2153
2154         if (!tv || !tv->is_track()) {
2155                 /* To make sure we hide the verbose canvas cursor when the mouse is
2156                    not held over an audiotrack.
2157                  */
2158                 _editor->verbose_cursor()->hide ();
2159                 return;
2160         }
2161
2162         framepos_t where = adjusted_current_frame (event);
2163         assert (where >= 0);
2164         framepos_t after;
2165         double delta = compute_x_delta (event, &after);
2166
2167         framecnt_t amount = _editor->pixel_to_sample (delta);
2168
2169         if (allow_moves_across_tracks) {
2170                 // all the originally selected regions were on the same track
2171
2172                 framecnt_t adjust = 0;
2173                 if (prev_tav && tv != prev_tav) {
2174                         // dragged onto a different track
2175                         // remove the unselected regions from _views, restore them to their original positions
2176                         // and add the regions after the drop point on the new playlist to _views instead.
2177                         // undo the effect of rippling the previous playlist, and include the effect of removing
2178                         // the dragged region(s) from this track
2179
2180                         remove_unselected_from_views (prev_amount, false);
2181                         // ripple previous playlist according to the regions that have been removed onto the new playlist
2182                         prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2183                         prev_amount = 0;
2184
2185                         // move just the selected regions
2186                         RegionMoveDrag::motion(event, first_move);
2187
2188                         // ensure that the ripple operation on the new playlist inserts selection_length time
2189                         adjust = selection_length;
2190                         // ripple the new current playlist
2191                         tv->playlist()->ripple (where, amount+adjust, exclude);
2192
2193                         // add regions after point where drag entered this track to subsequent ripples
2194                         add_all_after_to_views (tv, where, _editor->selection->regions, true);
2195
2196                 } else {
2197                         // motion on same track
2198                         RegionMoveDrag::motion(event, first_move);
2199                 }
2200                 prev_tav = tv;
2201
2202                 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2203                 prev_position = where;
2204         } else {
2205                 // selection encompasses multiple tracks - just drag
2206                 // cross-track drags are forbidden
2207                 RegionMoveDrag::motion(event, first_move);
2208         }
2209
2210         if (!_x_constrained) {
2211                 prev_amount += amount;
2212         }
2213
2214         _last_frame_position = after;
2215 }
2216
2217 void
2218 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2219 {
2220         if (!movement_occurred) {
2221
2222                 /* just a click */
2223
2224                 if (was_double_click() && !_views.empty()) {
2225                         DraggingView dv = _views.front();
2226                         dv.view->show_region_editor ();
2227
2228                 }
2229
2230                 return;
2231         }
2232
2233         _editor->begin_reversible_command(_("Ripple drag"));
2234
2235         // remove the regions being rippled from the dragging view, updating them to
2236         // their new positions
2237         remove_unselected_from_views (prev_amount, true);
2238
2239         if (allow_moves_across_tracks) {
2240                 if (orig_tav) {
2241                         // if regions were dragged across tracks, we've rippled any later
2242                         // regions on the track the regions were dragged off, so we need
2243                         // to add the original track to the undo record
2244                         orig_tav->playlist()->clear_changes();
2245                         vector<Command*> cmds;
2246                         orig_tav->playlist()->rdiff (cmds);
2247                         _editor->session()->add_commands (cmds);
2248                 }
2249                 if (prev_tav && prev_tav != orig_tav) {
2250                         prev_tav->playlist()->clear_changes();
2251                         vector<Command*> cmds;
2252                         prev_tav->playlist()->rdiff (cmds);
2253                         _editor->session()->add_commands (cmds);
2254                 }
2255         } else {
2256                 // selection spanned multiple tracks - all will need adding to undo record
2257
2258                 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2259                 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2260
2261                 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2262                         (*pi)->clear_changes();
2263                         vector<Command*> cmds;
2264                         (*pi)->rdiff (cmds);
2265                         _editor->session()->add_commands (cmds);
2266                 }
2267         }
2268
2269         // other modified playlists are added to undo by RegionMoveDrag::finished()
2270         RegionMoveDrag::finished (event, movement_occurred);
2271         _editor->commit_reversible_command();
2272 }
2273
2274 void
2275 RegionRippleDrag::aborted (bool movement_occurred)
2276 {
2277         RegionMoveDrag::aborted (movement_occurred);
2278         _views.clear ();
2279 }
2280
2281
2282 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2283         : Drag (e, i),
2284           _view (dynamic_cast<MidiTimeAxisView*> (v))
2285 {
2286         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2287
2288         assert (_view);
2289 }
2290
2291 void
2292 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2293 {
2294         if (first_move) {
2295                 _region = add_midi_region (_view);
2296                 _view->playlist()->freeze ();
2297         } else {
2298                 if (_region) {
2299                         framepos_t const f = adjusted_current_frame (event);
2300                         if (f < grab_frame()) {
2301                                 _region->set_position (f);
2302                         }
2303
2304                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2305                            so that if this region is duplicated, its duplicate starts on
2306                            a snap point rather than 1 frame after a snap point.  Otherwise things get
2307                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
2308                            place snapped notes at the start of the region.
2309                         */
2310
2311                         framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2312                         _region->set_length (len < 1 ? 1 : len);
2313                 }
2314         }
2315 }
2316
2317 void
2318 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2319 {
2320         if (!movement_occurred) {
2321                 add_midi_region (_view);
2322         } else {
2323                 _view->playlist()->thaw ();
2324         }
2325 }
2326
2327 void
2328 RegionCreateDrag::aborted (bool)
2329 {
2330         if (_region) {
2331                 _view->playlist()->thaw ();
2332         }
2333
2334         /* XXX */
2335 }
2336
2337 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2338         : Drag (e, i)
2339         , region (0)
2340         , relative (false)
2341         , at_front (true)
2342         , _snap_delta (0)
2343 {
2344         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2345 }
2346
2347 void
2348 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2349 {
2350         Gdk::Cursor* cursor;
2351         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2352         assert (cnote);
2353         float x_fraction = cnote->mouse_x_fraction ();
2354
2355         if (x_fraction > 0.0 && x_fraction < 0.25) {
2356                 cursor = _editor->cursors()->left_side_trim;
2357                 at_front = true;
2358         } else  {
2359                 cursor = _editor->cursors()->right_side_trim;
2360                 at_front = false;
2361         }
2362
2363         Drag::start_grab (event, cursor);
2364
2365         region = &cnote->region_view();
2366
2367         double temp;
2368         temp = region->snap_to_pixel (cnote->x0 (), true);
2369         _snap_delta = temp - cnote->x0 ();
2370
2371         _item->grab ();
2372
2373         if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2374                 relative = false;
2375         } else {
2376                 relative = true;
2377         }
2378         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2379         if (ms.size() > 1) {
2380                 /* has to be relative, may make no sense otherwise */
2381                 relative = true;
2382         }
2383         /* select this note; if it is already selected, preserve the existing selection,
2384            otherwise make this note the only one selected.
2385         */
2386         region->note_selected (cnote, cnote->selected ());
2387 }
2388
2389 void
2390 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2391 {
2392         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2393         if (first_move) {
2394                 _editor->begin_reversible_command (_("resize notes"));
2395
2396                 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2397                         MidiRegionSelection::iterator next;
2398                         next = r;
2399                         ++next;
2400                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2401                         if (mrv) {
2402                                 mrv->begin_resizing (at_front);
2403                         }
2404                         r = next;
2405                 }
2406         }
2407
2408         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2409                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2410                 assert (nb);
2411                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2412                 if (mrv) {
2413                         double sd = 0.0;
2414                         bool snap = true;
2415                         bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2416
2417                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2418                                 if (_editor->snap_mode () != SnapOff) {
2419                                         snap = false;
2420                                 }
2421                         } else {
2422                                 if (_editor->snap_mode () == SnapOff) {
2423                                         snap = false;
2424                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2425                                         if (apply_snap_delta) {
2426                                                 snap = true;
2427                                         }
2428                                 }
2429                         }
2430
2431                         if (apply_snap_delta) {
2432                                 sd = _snap_delta;
2433                         }
2434
2435                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2436                 }
2437         }
2438 }
2439
2440 void
2441 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2442 {
2443         if (!movement_occurred) {
2444                 return;
2445         }
2446
2447         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2448         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2449                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2450                 assert (nb);
2451                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2452                 double sd = 0.0;
2453                 bool snap = true;
2454                 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2455                 if (mrv) {
2456                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2457                                 if (_editor->snap_mode () != SnapOff) {
2458                                         snap = false;
2459                                 }
2460                         } else {
2461                                 if (_editor->snap_mode () == SnapOff) {
2462                                         snap = false;
2463                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2464                                         if (apply_snap_delta) {
2465                                                 snap = true;
2466                                         }
2467                                 }
2468                         }
2469
2470                         if (apply_snap_delta) {
2471                                 sd = _snap_delta;
2472                         }
2473
2474                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2475                 }
2476         }
2477
2478         _editor->commit_reversible_command ();
2479 }
2480
2481 void
2482 NoteResizeDrag::aborted (bool)
2483 {
2484         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2485         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2486                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2487                 if (mrv) {
2488                         mrv->abort_resizing ();
2489                 }
2490         }
2491 }
2492
2493 AVDraggingView::AVDraggingView (RegionView* v)
2494         : view (v)
2495 {
2496         initial_position = v->region()->position ();
2497 }
2498
2499 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2500         : Drag (e, i)
2501 {
2502         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2503
2504         RegionSelection rs;
2505         TrackViewList empty;
2506         empty.clear();
2507         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2508         std::list<RegionView*> views = rs.by_layer();
2509
2510         _stuck = false;
2511         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2512                 RegionView* rv = (*i);
2513                 if (!rv->region()->video_locked()) {
2514                         continue;
2515                 }
2516                 if (rv->region()->locked()) {
2517                         _stuck = true;
2518                 }
2519                 _views.push_back (AVDraggingView (rv));
2520         }
2521 }
2522
2523 void
2524 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2525 {
2526         Drag::start_grab (event);
2527         if (_editor->session() == 0) {
2528                 return;
2529         }
2530
2531         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2532                 _stuck = false;
2533                 _views.clear();
2534         }
2535
2536         if (_stuck) {
2537                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2538                 return;
2539         }
2540
2541         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2542         _max_backwards_drag = (
2543                           ARDOUR_UI::instance()->video_timeline->get_duration()
2544                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2545                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2546                         );
2547
2548         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2549                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2550                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2551                 }
2552         }
2553         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2554
2555         char buf[128];
2556         Timecode::Time timecode;
2557         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2558         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);
2559         show_verbose_cursor_text (buf);
2560 }
2561
2562 void
2563 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2564 {
2565         if (_editor->session() == 0) {
2566                 return;
2567         }
2568         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2569                 return;
2570         }
2571         if (_stuck) {
2572                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2573                 return;
2574         }
2575
2576         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2577         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2578
2579         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2580                 dt = - _max_backwards_drag;
2581         }
2582
2583         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2584         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2585
2586         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2587                 RegionView* rv = i->view;
2588                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2589                 if (first_move) {
2590                         rv->drag_start ();
2591                         rv->region()->clear_changes ();
2592                         rv->region()->suspend_property_changes();
2593                 }
2594                 rv->region()->set_position(i->initial_position + dt);
2595                 rv->region_changed(ARDOUR::Properties::position);
2596         }
2597
2598         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2599         Timecode::Time timecode;
2600         Timecode::Time timediff;
2601         char buf[128];
2602         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2603         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2604         snprintf (buf, sizeof (buf),
2605                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2606                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2607                         , _("Video Start:"),
2608                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2609                         , _("Diff:"),
2610                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2611                                 );
2612         show_verbose_cursor_text (buf);
2613 }
2614
2615 void
2616 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2617 {
2618         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2619                 return;
2620         }
2621         if (_stuck) {
2622                 return;
2623         }
2624
2625         if (!movement_occurred || ! _editor->session()) {
2626                 return;
2627         }
2628
2629         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2630
2631         _editor->begin_reversible_command (_("Move Video"));
2632
2633         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2634         ARDOUR_UI::instance()->video_timeline->save_undo();
2635         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2636         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2637
2638         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2639                 i->view->drag_end();
2640                 i->view->region()->resume_property_changes ();
2641
2642                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2643         }
2644
2645         _editor->session()->maybe_update_session_range(
2646                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2647                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2648                         );
2649
2650
2651         _editor->commit_reversible_command ();
2652 }
2653
2654 void
2655 VideoTimeLineDrag::aborted (bool)
2656 {
2657         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2658                 return;
2659         }
2660         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2661         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2662
2663         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2664                 i->view->region()->resume_property_changes ();
2665                 i->view->region()->set_position(i->initial_position);
2666         }
2667 }
2668
2669 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2670         : RegionDrag (e, i, p, v)
2671         , _preserve_fade_anchor (preserve_fade_anchor)
2672         , _jump_position_when_done (false)
2673 {
2674         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2675 }
2676
2677 void
2678 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2679 {
2680         double speed = 1.0;
2681         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2682         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2683
2684         if (tv && tv->is_track()) {
2685                 speed = tv->track()->speed();
2686         }
2687
2688         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2689         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2690         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2691
2692         framepos_t const pf = adjusted_current_frame (event);
2693         setup_snap_delta (region_start);
2694
2695         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2696                 /* Move the contents of the region around without changing the region bounds */
2697                 _operation = ContentsTrim;
2698                 Drag::start_grab (event, _editor->cursors()->trimmer);
2699         } else {
2700                 /* These will get overridden for a point trim.*/
2701                 if (pf < (region_start + region_length/2)) {
2702                         /* closer to front */
2703                         _operation = StartTrim;
2704                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2705                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2706                         } else {
2707                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2708                         }
2709                 } else {
2710                         /* closer to end */
2711                         _operation = EndTrim;
2712                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2713                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2714                         } else {
2715                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2716                         }
2717                 }
2718         }
2719         /* jump trim disabled for now
2720         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2721                 _jump_position_when_done = true;
2722         }
2723         */
2724
2725         switch (_operation) {
2726         case StartTrim:
2727                 show_verbose_cursor_time (region_start);
2728                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2729                         i->view->trim_front_starting ();
2730                 }
2731                 break;
2732         case EndTrim:
2733                 show_verbose_cursor_duration (region_start, region_end);
2734                 break;
2735         case ContentsTrim:
2736                 show_verbose_cursor_time (pf);
2737                 break;
2738         }
2739
2740         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2741                 i->view->region()->suspend_property_changes ();
2742         }
2743 }
2744
2745 void
2746 TrimDrag::motion (GdkEvent* event, bool first_move)
2747 {
2748         RegionView* rv = _primary;
2749
2750         double speed = 1.0;
2751         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2752         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2753         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2754         frameoffset_t frame_delta = 0;
2755
2756         if (tv && tv->is_track()) {
2757                 speed = tv->track()->speed();
2758         }
2759         framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2760         framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2761
2762         if (first_move) {
2763
2764                 string trim_type;
2765
2766                 switch (_operation) {
2767                 case StartTrim:
2768                         trim_type = "Region start trim";
2769                         break;
2770                 case EndTrim:
2771                         trim_type = "Region end trim";
2772                         break;
2773                 case ContentsTrim:
2774                         trim_type = "Region content trim";
2775                         break;
2776                 default:
2777                         assert(0);
2778                         break;
2779                 }
2780
2781                 _editor->begin_reversible_command (trim_type);
2782
2783                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2784                         RegionView* rv = i->view;
2785                         rv->enable_display (false);
2786                         rv->region()->playlist()->clear_owned_changes ();
2787
2788                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2789
2790                         if (arv) {
2791                                 arv->temporarily_hide_envelope ();
2792                                 arv->drag_start ();
2793                         }
2794
2795                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2796                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2797
2798                         if (insert_result.second) {
2799                                 pl->freeze();
2800                         }
2801                 }
2802         }
2803
2804         bool non_overlap_trim = false;
2805
2806         if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2807                 non_overlap_trim = true;
2808         }
2809
2810         /* contstrain trim to fade length */
2811         if (_preserve_fade_anchor) {
2812                 switch (_operation) {
2813                         case StartTrim:
2814                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2815                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2816                                         if (!arv) continue;
2817                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2818                                         if (ar->locked()) continue;
2819                                         framecnt_t len = ar->fade_in()->back()->when;
2820                                         if (len < dt) dt = min(dt, len);
2821                                 }
2822                                 break;
2823                         case EndTrim:
2824                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2825                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2826                                         if (!arv) continue;
2827                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2828                                         if (ar->locked()) continue;
2829                                         framecnt_t len = ar->fade_out()->back()->when;
2830                                         if (len < -dt) dt = max(dt, -len);
2831                                 }
2832                                 break;
2833                         case ContentsTrim:
2834                                 break;
2835                 }
2836         }
2837
2838
2839         switch (_operation) {
2840         case StartTrim:
2841                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2842                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2843                         if (changed && _preserve_fade_anchor) {
2844                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2845                                 if (arv) {
2846                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2847                                         framecnt_t len = ar->fade_in()->back()->when;
2848                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2849                                         framepos_t new_length = len - diff;
2850                                         i->anchored_fade_length = min (ar->length(), new_length);
2851                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2852                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2853                                 }
2854                         }
2855                 }
2856                 break;
2857
2858         case EndTrim:
2859                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2860                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2861                         if (changed && _preserve_fade_anchor) {
2862                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2863                                 if (arv) {
2864                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2865                                         framecnt_t len = ar->fade_out()->back()->when;
2866                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2867                                         framepos_t new_length = len + diff;
2868                                         i->anchored_fade_length = min (ar->length(), new_length);
2869                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2870                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2871                                 }
2872                         }
2873                 }
2874                 break;
2875
2876         case ContentsTrim:
2877                 {
2878                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2879
2880                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2881                                 i->view->move_contents (frame_delta);
2882                         }
2883                 }
2884                 break;
2885         }
2886
2887         switch (_operation) {
2888         case StartTrim:
2889                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2890                 break;
2891         case EndTrim:
2892                 show_verbose_cursor_duration ((framepos_t)  rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2893                 break;
2894         case ContentsTrim:
2895                 // show_verbose_cursor_time (frame_delta);
2896                 break;
2897         }
2898 }
2899
2900 void
2901 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2902 {
2903         if (movement_occurred) {
2904                 motion (event, false);
2905
2906                 if (_operation == StartTrim) {
2907                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2908                                 {
2909                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2910                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2911                                            needs to be zero in the undo record.
2912                                         */
2913                                         i->view->trim_front_ending ();
2914                                 }
2915                                 if (_preserve_fade_anchor) {
2916                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2917                                         if (arv) {
2918                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2919                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2920                                                 ar->set_fade_in_length(i->anchored_fade_length);
2921                                                 ar->set_fade_in_active(true);
2922                                         }
2923                                 }
2924                                 if (_jump_position_when_done) {
2925                                         i->view->region()->set_position (i->initial_position);
2926                                 }
2927                         }
2928                 } else if (_operation == EndTrim) {
2929                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2930                                 if (_preserve_fade_anchor) {
2931                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2932                                         if (arv) {
2933                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2934                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2935                                                 ar->set_fade_out_length(i->anchored_fade_length);
2936                                                 ar->set_fade_out_active(true);
2937                                         }
2938                                 }
2939                                 if (_jump_position_when_done) {
2940                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
2941                                 }
2942                         }
2943                 }
2944
2945                 if (!_views.empty()) {
2946                         if (_operation == StartTrim) {
2947                                 _editor->maybe_locate_with_edit_preroll(
2948                                         _views.begin()->view->region()->position());
2949                         }
2950                         if (_operation == EndTrim) {
2951                                 _editor->maybe_locate_with_edit_preroll(
2952                                         _views.begin()->view->region()->position() +
2953                                         _views.begin()->view->region()->length());
2954                         }
2955                 }
2956
2957                 if (!_editor->selection->selected (_primary)) {
2958                         _primary->thaw_after_trim ();
2959                 } else {
2960
2961                         set<boost::shared_ptr<Playlist> > diffed_playlists;
2962
2963                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2964                                 i->view->thaw_after_trim ();
2965                                 i->view->enable_display (true);
2966
2967                                 /* Trimming one region may affect others on the playlist, so we need
2968                                    to get undo Commands from the whole playlist rather than just the
2969                                    region.  Use diffed_playlists to make sure we don't diff a given
2970                                    playlist more than once.
2971                                 */
2972                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2973                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2974                                         vector<Command*> cmds;
2975                                         p->rdiff (cmds);
2976                                         _editor->session()->add_commands (cmds);
2977                                         diffed_playlists.insert (p);
2978                                 }
2979                         }
2980                 }
2981
2982                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2983                         (*p)->thaw ();
2984                 }
2985
2986                 _editor->motion_frozen_playlists.clear ();
2987                 _editor->commit_reversible_command();
2988
2989         } else {
2990                 /* no mouse movement */
2991                 _editor->point_trim (event, adjusted_current_frame (event));
2992         }
2993
2994         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2995                 if (_operation == StartTrim) {
2996                         i->view->trim_front_ending ();
2997                 }
2998
2999                 i->view->region()->resume_property_changes ();
3000         }
3001 }
3002
3003 void
3004 TrimDrag::aborted (bool movement_occurred)
3005 {
3006         /* Our motion method is changing model state, so use the Undo system
3007            to cancel.  Perhaps not ideal, as this will leave an Undo point
3008            behind which may be slightly odd from the user's point of view.
3009         */
3010
3011         finished (0, true);
3012
3013         if (movement_occurred) {
3014                 _editor->undo ();
3015         }
3016
3017         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3018                 i->view->region()->resume_property_changes ();
3019         }
3020 }
3021
3022 void
3023 TrimDrag::setup_pointer_frame_offset ()
3024 {
3025         list<DraggingView>::iterator i = _views.begin ();
3026         while (i != _views.end() && i->view != _primary) {
3027                 ++i;
3028         }
3029
3030         if (i == _views.end()) {
3031                 return;
3032         }
3033
3034         switch (_operation) {
3035         case StartTrim:
3036                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3037                 break;
3038         case EndTrim:
3039                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3040                 break;
3041         case ContentsTrim:
3042                 break;
3043         }
3044 }
3045
3046 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3047         : Drag (e, i),
3048           _copy (c)
3049 {
3050         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3051         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3052         assert (_marker);
3053 }
3054
3055 void
3056 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3057 {
3058         Drag::start_grab (event, cursor);
3059         show_verbose_cursor_time (adjusted_current_frame(event));
3060 }
3061
3062 void
3063 MeterMarkerDrag::setup_pointer_frame_offset ()
3064 {
3065         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3066 }
3067
3068 void
3069 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3070 {
3071         if (!_marker->meter().movable()) {
3072                 return;
3073         }
3074
3075         if (first_move) {
3076
3077                 // create a dummy marker for visual representation of moving the
3078                 // section, because whether its a copy or not, we're going to
3079                 // leave or lose the original marker (leave if its a copy; lose if its
3080                 // not, because we'll remove it from the map).
3081
3082                 MeterSection section (_marker->meter());
3083
3084                 if (!section.movable()) {
3085                         return;
3086                 }
3087
3088                 char name[64];
3089                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3090
3091                 _marker = new MeterMarker (
3092                         *_editor,
3093                         *_editor->meter_group,
3094                         ARDOUR_UI::config()->color ("meter marker"),
3095                         name,
3096                         *new MeterSection (_marker->meter())
3097                 );
3098
3099                 /* use the new marker for the grab */
3100                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3101
3102                 if (!_copy) {
3103                         TempoMap& map (_editor->session()->tempo_map());
3104                         /* get current state */
3105                         before_state = &map.get_state();
3106                         /* remove the section while we drag it */
3107                         map.remove_meter (section, true);
3108                 }
3109         }
3110
3111         framepos_t const pf = adjusted_current_frame (event);
3112
3113         _marker->set_position (pf);
3114         show_verbose_cursor_time (pf);
3115 }
3116
3117 void
3118 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3119 {
3120         if (!movement_occurred) {
3121                 if (was_double_click()) {
3122                         _editor->edit_meter_marker (*_marker);
3123                 }
3124                 return;
3125         }
3126
3127         if (!_marker->meter().movable()) {
3128                 return;
3129         }
3130
3131         motion (event, false);
3132
3133         Timecode::BBT_Time when;
3134
3135         TempoMap& map (_editor->session()->tempo_map());
3136         map.bbt_time (last_pointer_frame(), when);
3137
3138         if (_copy == true) {
3139                 _editor->begin_reversible_command (_("copy meter mark"));
3140                 XMLNode &before = map.get_state();
3141                 map.add_meter (_marker->meter(), when);
3142                 XMLNode &after = map.get_state();
3143                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3144                 _editor->commit_reversible_command ();
3145
3146         } else {
3147                 _editor->begin_reversible_command (_("move meter mark"));
3148
3149                 /* we removed it before, so add it back now */
3150
3151                 map.add_meter (_marker->meter(), when);
3152                 XMLNode &after = map.get_state();
3153                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3154                 _editor->commit_reversible_command ();
3155         }
3156
3157         // delete the dummy marker we used for visual representation while moving.
3158         // a new visual marker will show up automatically.
3159         delete _marker;
3160 }
3161
3162 void
3163 MeterMarkerDrag::aborted (bool moved)
3164 {
3165         _marker->set_position (_marker->meter().frame ());
3166
3167         if (moved) {
3168                 TempoMap& map (_editor->session()->tempo_map());
3169                 /* we removed it before, so add it back now */
3170                 map.add_meter (_marker->meter(), _marker->meter().frame());
3171                 // delete the dummy marker we used for visual representation while moving.
3172                 // a new visual marker will show up automatically.
3173                 delete _marker;
3174         }
3175 }
3176
3177 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3178         : Drag (e, i),
3179           _copy (c)
3180 {
3181         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3182
3183         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3184         assert (_marker);
3185 }
3186
3187 void
3188 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3189 {
3190         Drag::start_grab (event, cursor);
3191         show_verbose_cursor_time (adjusted_current_frame (event));
3192 }
3193
3194 void
3195 TempoMarkerDrag::setup_pointer_frame_offset ()
3196 {
3197         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3198 }
3199
3200 void
3201 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3202 {
3203         if (!_marker->tempo().movable()) {
3204                 return;
3205         }
3206
3207         if (first_move) {
3208
3209                 // create a dummy marker for visual representation of moving the
3210                 // section, because whether its a copy or not, we're going to
3211                 // leave or lose the original marker (leave if its a copy; lose if its
3212                 // not, because we'll remove it from the map).
3213
3214                 // create a dummy marker for visual representation of moving the copy.
3215                 // The actual copying is not done before we reach the finish callback.
3216
3217                 char name[64];
3218                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3219
3220                 TempoSection section (_marker->tempo());
3221
3222                 _marker = new TempoMarker (
3223                         *_editor,
3224                         *_editor->tempo_group,
3225                         ARDOUR_UI::config()->color ("tempo marker"),
3226                         name,
3227                         *new TempoSection (_marker->tempo())
3228                         );
3229
3230                 /* use the new marker for the grab */
3231                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3232
3233                 if (!_copy) {
3234                         TempoMap& map (_editor->session()->tempo_map());
3235                         /* get current state */
3236                         before_state = &map.get_state();
3237                         /* remove the section while we drag it */
3238                         map.remove_tempo (section, true);
3239                 }
3240         }
3241
3242         framepos_t const pf = adjusted_current_frame (event);
3243         _marker->set_position (pf);
3244         show_verbose_cursor_time (pf);
3245 }
3246
3247 void
3248 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3249 {
3250         if (!movement_occurred) {
3251                 if (was_double_click()) {
3252                         _editor->edit_tempo_marker (*_marker);
3253                 }
3254                 return;
3255         }
3256
3257         if (!_marker->tempo().movable()) {
3258                 return;
3259         }
3260
3261         motion (event, false);
3262
3263         TempoMap& map (_editor->session()->tempo_map());
3264         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3265         Timecode::BBT_Time when;
3266
3267         map.bbt_time (beat_time, when);
3268
3269         if (_copy == true) {
3270                 _editor->begin_reversible_command (_("copy tempo mark"));
3271                 XMLNode &before = map.get_state();
3272                 map.add_tempo (_marker->tempo(), when);
3273                 XMLNode &after = map.get_state();
3274                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3275                 _editor->commit_reversible_command ();
3276
3277         } else {
3278                 _editor->begin_reversible_command (_("move tempo mark"));
3279                 /* we removed it before, so add it back now */
3280                 map.add_tempo (_marker->tempo(), when);
3281                 XMLNode &after = map.get_state();
3282                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3283                 _editor->commit_reversible_command ();
3284         }
3285
3286         // delete the dummy marker we used for visual representation while moving.
3287         // a new visual marker will show up automatically.
3288         delete _marker;
3289 }
3290
3291 void
3292 TempoMarkerDrag::aborted (bool moved)
3293 {
3294         _marker->set_position (_marker->tempo().frame());
3295         if (moved) {
3296                 TempoMap& map (_editor->session()->tempo_map());
3297                 /* we removed it before, so add it back now */
3298                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3299                 // delete the dummy marker we used for visual representation while moving.
3300                 // a new visual marker will show up automatically.
3301                 delete _marker;
3302         }
3303 }
3304
3305 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3306         : Drag (e, &c.track_canvas_item(), false)
3307         , _cursor (c)
3308         , _stop (s)
3309 {
3310         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3311 }
3312
3313 /** Do all the things we do when dragging the playhead to make it look as though
3314  *  we have located, without actually doing the locate (because that would cause
3315  *  the diskstream buffers to be refilled, which is too slow).
3316  */
3317 void
3318 CursorDrag::fake_locate (framepos_t t)
3319 {
3320         if (_editor->session () == 0) {
3321                 return;
3322         }
3323
3324         _editor->playhead_cursor->set_position (t);
3325
3326         Session* s = _editor->session ();
3327         if (s->timecode_transmission_suspended ()) {
3328                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3329                 /* This is asynchronous so it will be sent "now"
3330                  */
3331                 s->send_mmc_locate (f);
3332                 /* These are synchronous and will be sent during the next
3333                    process cycle
3334                 */
3335                 s->queue_full_time_code ();
3336                 s->queue_song_position_pointer ();
3337         }
3338
3339         show_verbose_cursor_time (t);
3340         _editor->UpdateAllTransportClocks (t);
3341 }
3342
3343 void
3344 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3345 {
3346         Drag::start_grab (event, c);
3347         setup_snap_delta (_editor->playhead_cursor->current_frame ());
3348
3349         _grab_zoom = _editor->samples_per_pixel;
3350
3351         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3352
3353         _editor->snap_to_with_modifier (where, event);
3354
3355         _editor->_dragging_playhead = true;
3356
3357         Session* s = _editor->session ();
3358
3359         /* grab the track canvas item as well */
3360
3361         _cursor.track_canvas_item().grab();
3362
3363         if (s) {
3364                 if (_was_rolling && _stop) {
3365                         s->request_stop ();
3366                 }
3367
3368                 if (s->is_auditioning()) {
3369                         s->cancel_audition ();
3370                 }
3371
3372
3373                 if (AudioEngine::instance()->connected()) {
3374
3375                         /* do this only if we're the engine is connected
3376                          * because otherwise this request will never be
3377                          * serviced and we'll busy wait forever. likewise,
3378                          * notice if we are disconnected while waiting for the
3379                          * request to be serviced.
3380                          */
3381
3382                         s->request_suspend_timecode_transmission ();
3383                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3384                                 /* twiddle our thumbs */
3385                         }
3386                 }
3387         }
3388
3389         fake_locate (where - snap_delta (event->button.state));
3390 }
3391
3392 void
3393 CursorDrag::motion (GdkEvent* event, bool)
3394 {
3395         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3396         _editor->snap_to_with_modifier (where, event);
3397         if (where != last_pointer_frame()) {
3398                 fake_locate (where - snap_delta (event->button.state));
3399         }
3400 }
3401
3402 void
3403 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3404 {
3405         _editor->_dragging_playhead = false;
3406
3407         _cursor.track_canvas_item().ungrab();
3408
3409         if (!movement_occurred && _stop) {
3410                 return;
3411         }
3412
3413         motion (event, false);
3414
3415         Session* s = _editor->session ();
3416         if (s) {
3417                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3418                 _editor->_pending_locate_request = true;
3419                 s->request_resume_timecode_transmission ();
3420         }
3421 }
3422
3423 void
3424 CursorDrag::aborted (bool)
3425 {
3426         _cursor.track_canvas_item().ungrab();
3427
3428         if (_editor->_dragging_playhead) {
3429                 _editor->session()->request_resume_timecode_transmission ();
3430                 _editor->_dragging_playhead = false;
3431         }
3432
3433         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3434 }
3435
3436 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3437         : RegionDrag (e, i, p, v)
3438 {
3439         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3440 }
3441
3442 void
3443 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3444 {
3445         Drag::start_grab (event, cursor);
3446
3447         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3448         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3449         setup_snap_delta (r->position ());
3450
3451         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3452 }
3453
3454 void
3455 FadeInDrag::setup_pointer_frame_offset ()
3456 {
3457         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3458         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3459         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3460 }
3461
3462 void
3463 FadeInDrag::motion (GdkEvent* event, bool)
3464 {
3465         framecnt_t fade_length;
3466
3467         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3468         _editor->snap_to_with_modifier (pos, event);
3469         pos -= snap_delta (event->button.state);
3470
3471         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3472
3473         if (pos < (region->position() + 64)) {
3474                 fade_length = 64; // this should be a minimum defined somewhere
3475         } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3476                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3477         } else {
3478                 fade_length = pos - region->position();
3479         }
3480
3481         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3482
3483                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3484
3485                 if (!tmp) {
3486                         continue;
3487                 }
3488
3489                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3490         }
3491
3492         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3493 }
3494
3495 void
3496 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3497 {
3498         if (!movement_occurred) {
3499                 return;
3500         }
3501
3502         framecnt_t fade_length;
3503         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3504         _editor->snap_to_with_modifier (pos, event);
3505         pos -= snap_delta (event->button.state);
3506
3507         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3508
3509         if (pos < (region->position() + 64)) {
3510                 fade_length = 64; // this should be a minimum defined somewhere
3511         } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3512                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3513         } else {
3514                 fade_length = pos - region->position();
3515         }
3516
3517         bool in_command = false;
3518
3519         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3520
3521                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3522
3523                 if (!tmp) {
3524                         continue;
3525                 }
3526
3527                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3528                 XMLNode &before = alist->get_state();
3529
3530                 tmp->audio_region()->set_fade_in_length (fade_length);
3531                 tmp->audio_region()->set_fade_in_active (true);
3532
3533                 if (!in_command) {
3534                         _editor->begin_reversible_command (_("change fade in length"));
3535                         in_command = true;
3536                 }
3537                 XMLNode &after = alist->get_state();
3538                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3539         }
3540
3541         if (in_command) {
3542                 _editor->commit_reversible_command ();
3543         }
3544 }
3545
3546 void
3547 FadeInDrag::aborted (bool)
3548 {
3549         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3550                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3551
3552                 if (!tmp) {
3553                         continue;
3554                 }
3555
3556                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3557         }
3558 }
3559
3560 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3561         : RegionDrag (e, i, p, v)
3562 {
3563         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3564 }
3565
3566 void
3567 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3568 {
3569         Drag::start_grab (event, cursor);
3570
3571         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3572         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3573         setup_snap_delta (r->last_frame ());
3574
3575         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3576 }
3577
3578 void
3579 FadeOutDrag::setup_pointer_frame_offset ()
3580 {
3581         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3582         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3583         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3584 }
3585
3586 void
3587 FadeOutDrag::motion (GdkEvent* event, bool)
3588 {
3589         framecnt_t fade_length;
3590
3591         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3592         _editor->snap_to_with_modifier (pos, event);
3593         pos -= snap_delta (event->button.state);
3594
3595         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3596
3597         if (pos > (region->last_frame() - 64)) {
3598                 fade_length = 64; // this should really be a minimum fade defined somewhere
3599         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3600                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3601         } else {
3602                 fade_length = region->last_frame() - pos;
3603         }
3604
3605         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3606
3607                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3608
3609                 if (!tmp) {
3610                         continue;
3611                 }
3612
3613                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3614         }
3615
3616         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3617 }
3618
3619 void
3620 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3621 {
3622         if (!movement_occurred) {
3623                 return;
3624         }
3625
3626         framecnt_t fade_length;
3627
3628         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3629         _editor->snap_to_with_modifier (pos, event);
3630         pos -= snap_delta (event->button.state);
3631
3632         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3633
3634         if (pos > (region->last_frame() - 64)) {
3635                 fade_length = 64; // this should really be a minimum fade defined somewhere
3636         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3637                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3638         } else {
3639                 fade_length = region->last_frame() - pos;
3640         }
3641
3642         bool in_command = false;
3643
3644         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3645
3646                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3647
3648                 if (!tmp) {
3649                         continue;
3650                 }
3651
3652                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3653                 XMLNode &before = alist->get_state();
3654
3655                 tmp->audio_region()->set_fade_out_length (fade_length);
3656                 tmp->audio_region()->set_fade_out_active (true);
3657
3658                 if (!in_command) {
3659                         _editor->begin_reversible_command (_("change fade out length"));
3660                         in_command = true;
3661                 }
3662                 XMLNode &after = alist->get_state();
3663                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3664         }
3665
3666         if (in_command) {
3667                 _editor->commit_reversible_command ();
3668         }
3669 }
3670
3671 void
3672 FadeOutDrag::aborted (bool)
3673 {
3674         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3675                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3676
3677                 if (!tmp) {
3678                         continue;
3679                 }
3680
3681                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3682         }
3683 }
3684
3685 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3686         : Drag (e, i)
3687 {
3688         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3689
3690         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3691         assert (_marker);
3692
3693         _points.push_back (ArdourCanvas::Duple (0, 0));
3694         _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3695 }
3696
3697 MarkerDrag::~MarkerDrag ()
3698 {
3699         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3700                 delete i->location;
3701         }
3702 }
3703
3704 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3705 {
3706         location = new Location (*l);
3707         markers.push_back (m);
3708         move_both = false;
3709 }
3710
3711 void
3712 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3713 {
3714         Drag::start_grab (event, cursor);
3715
3716         bool is_start;
3717
3718         Location *location = _editor->find_location_from_marker (_marker, is_start);
3719         _editor->_dragging_edit_point = true;
3720
3721         update_item (location);
3722
3723         // _drag_line->show();
3724         // _line->raise_to_top();
3725
3726         if (is_start) {
3727                 show_verbose_cursor_time (location->start());
3728         } else {
3729                 show_verbose_cursor_time (location->end());
3730         }
3731
3732         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3733
3734         switch (op) {
3735         case Selection::Toggle:
3736                 /* we toggle on the button release */
3737                 break;
3738         case Selection::Set:
3739                 if (!_editor->selection->selected (_marker)) {
3740                         _editor->selection->set (_marker);
3741                 }
3742                 break;
3743         case Selection::Extend:
3744         {
3745                 Locations::LocationList ll;
3746                 list<ArdourMarker*> to_add;
3747                 framepos_t s, e;
3748                 _editor->selection->markers.range (s, e);
3749                 s = min (_marker->position(), s);
3750                 e = max (_marker->position(), e);
3751                 s = min (s, e);
3752                 e = max (s, e);
3753                 if (e < max_framepos) {
3754                         ++e;
3755                 }
3756                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3757                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3758                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3759                         if (lm) {
3760                                 if (lm->start) {
3761                                         to_add.push_back (lm->start);
3762                                 }
3763                                 if (lm->end) {
3764                                         to_add.push_back (lm->end);
3765                                 }
3766                         }
3767                 }
3768                 if (!to_add.empty()) {
3769                         _editor->selection->add (to_add);
3770                 }
3771                 break;
3772         }
3773         case Selection::Add:
3774                 _editor->selection->add (_marker);
3775                 break;
3776         }
3777
3778         /* Set up copies for us to manipulate during the drag
3779          */
3780
3781         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3782
3783                 Location* l = _editor->find_location_from_marker (*i, is_start);
3784
3785                 if (!l) {
3786                         continue;
3787                 }
3788
3789                 if (l->is_mark()) {
3790                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3791                 } else {
3792                         /* range: check that the other end of the range isn't
3793                            already there.
3794                         */
3795                         CopiedLocationInfo::iterator x;
3796                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3797                                 if (*(*x).location == *l) {
3798                                         break;
3799                                 }
3800                         }
3801                         if (x == _copied_locations.end()) {
3802                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3803                         } else {
3804                                 (*x).markers.push_back (*i);
3805                                 (*x).move_both = true;
3806                         }
3807                 }
3808
3809         }
3810 }
3811
3812 void
3813 MarkerDrag::setup_pointer_frame_offset ()
3814 {
3815         bool is_start;
3816         Location *location = _editor->find_location_from_marker (_marker, is_start);
3817         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3818 }
3819
3820 void
3821 MarkerDrag::motion (GdkEvent* event, bool)
3822 {
3823         framecnt_t f_delta = 0;
3824         bool is_start;
3825         bool move_both = false;
3826         Location *real_location;
3827         Location *copy_location = 0;
3828
3829         framepos_t const newframe = adjusted_current_frame (event);
3830         framepos_t next = newframe;
3831
3832         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3833                 move_both = true;
3834         }
3835
3836         CopiedLocationInfo::iterator x;
3837
3838         /* find the marker we're dragging, and compute the delta */
3839
3840         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3841
3842                 copy_location = (*x).location;
3843
3844                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3845
3846                         /* this marker is represented by this
3847                          * CopiedLocationMarkerInfo
3848                          */
3849
3850                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3851                                 /* que pasa ?? */
3852                                 return;
3853                         }
3854
3855                         if (real_location->is_mark()) {
3856                                 f_delta = newframe - copy_location->start();
3857                         } else {
3858
3859
3860                                 switch (_marker->type()) {
3861                                 case ArdourMarker::SessionStart:
3862                                 case ArdourMarker::RangeStart:
3863                                 case ArdourMarker::LoopStart:
3864                                 case ArdourMarker::PunchIn:
3865                                         f_delta = newframe - copy_location->start();
3866                                         break;
3867
3868                                 case ArdourMarker::SessionEnd:
3869                                 case ArdourMarker::RangeEnd:
3870                                 case ArdourMarker::LoopEnd:
3871                                 case ArdourMarker::PunchOut:
3872                                         f_delta = newframe - copy_location->end();
3873                                         break;
3874                                 default:
3875                                         /* what kind of marker is this ? */
3876                                         return;
3877                                 }
3878                         }
3879
3880                         break;
3881                 }
3882         }
3883
3884         if (x == _copied_locations.end()) {
3885                 /* hmm, impossible - we didn't find the dragged marker */
3886                 return;
3887         }
3888
3889         /* now move them all */
3890
3891         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3892
3893                 copy_location = x->location;
3894
3895                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3896                         continue;
3897                 }
3898
3899                 if (real_location->locked()) {
3900                         continue;
3901                 }
3902
3903                 if (copy_location->is_mark()) {
3904
3905                         /* now move it */
3906
3907                         copy_location->set_start (copy_location->start() + f_delta);
3908
3909                 } else {
3910
3911                         framepos_t new_start = copy_location->start() + f_delta;
3912                         framepos_t new_end = copy_location->end() + f_delta;
3913
3914                         if (is_start) { // start-of-range marker
3915
3916                                 if (move_both || (*x).move_both) {
3917                                         copy_location->set_start (new_start);
3918                                         copy_location->set_end (new_end);
3919                                 } else  if (new_start < copy_location->end()) {
3920                                         copy_location->set_start (new_start);
3921                                 } else if (newframe > 0) {
3922                                         _editor->snap_to (next, RoundUpAlways, true);
3923                                         copy_location->set_end (next);
3924                                         copy_location->set_start (newframe);
3925                                 }
3926
3927                         } else { // end marker
3928
3929                                 if (move_both || (*x).move_both) {
3930                                         copy_location->set_end (new_end);
3931                                         copy_location->set_start (new_start);
3932                                 } else if (new_end > copy_location->start()) {
3933                                         copy_location->set_end (new_end);
3934                                 } else if (newframe > 0) {
3935                                         _editor->snap_to (next, RoundDownAlways, true);
3936                                         copy_location->set_start (next);
3937                                         copy_location->set_end (newframe);
3938                                 }
3939                         }
3940                 }
3941
3942                 update_item (copy_location);
3943
3944                 /* now lookup the actual GUI items used to display this
3945                  * location and move them to wherever the copy of the location
3946                  * is now. This means that the logic in ARDOUR::Location is
3947                  * still enforced, even though we are not (yet) modifying
3948                  * the real Location itself.
3949                  */
3950
3951                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3952
3953                 if (lm) {
3954                         lm->set_position (copy_location->start(), copy_location->end());
3955                 }
3956
3957         }
3958
3959         assert (!_copied_locations.empty());
3960
3961         show_verbose_cursor_time (newframe);
3962 }
3963
3964 void
3965 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3966 {
3967         if (!movement_occurred) {
3968
3969                 if (was_double_click()) {
3970                         _editor->rename_marker (_marker);
3971                         return;
3972                 }
3973
3974                 /* just a click, do nothing but finish
3975                    off the selection process
3976                 */
3977
3978                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3979
3980                 switch (op) {
3981                 case Selection::Set:
3982                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3983                                 _editor->selection->set (_marker);
3984                         }
3985                         break;
3986
3987                 case Selection::Toggle:
3988                         /* we toggle on the button release, click only */
3989                         _editor->selection->toggle (_marker);
3990                         break;
3991
3992                 case Selection::Extend:
3993                 case Selection::Add:
3994                         break;
3995                 }
3996
3997                 return;
3998         }
3999
4000         _editor->_dragging_edit_point = false;
4001
4002         XMLNode &before = _editor->session()->locations()->get_state();
4003         bool in_command = false;
4004
4005         MarkerSelection::iterator i;
4006         CopiedLocationInfo::iterator x;
4007         bool is_start;
4008
4009         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4010              x != _copied_locations.end() && i != _editor->selection->markers.end();
4011              ++i, ++x) {
4012
4013                 Location * location = _editor->find_location_from_marker (*i, is_start);
4014
4015                 if (location) {
4016
4017                         if (location->locked()) {
4018                                 continue;
4019                         }
4020                         if (!in_command) {
4021                                 _editor->begin_reversible_command ( _("move marker") );
4022                                 in_command = true;
4023                         }
4024                         if (location->is_mark()) {
4025                                 location->set_start (((*x).location)->start());
4026                         } else {
4027                                 location->set (((*x).location)->start(), ((*x).location)->end());
4028                         }
4029                 }
4030         }
4031
4032         if (in_command) {
4033                 XMLNode &after = _editor->session()->locations()->get_state();
4034                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4035                 _editor->commit_reversible_command ();
4036         }
4037 }
4038
4039 void
4040 MarkerDrag::aborted (bool movement_occured)
4041 {
4042         if (!movement_occured) {
4043                 return;
4044         }
4045
4046         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4047
4048                 /* move all markers to their original location */
4049
4050
4051                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4052
4053                         bool is_start;
4054                         Location * location = _editor->find_location_from_marker (*m, is_start);
4055
4056                         if (location) {
4057                                 (*m)->set_position (is_start ? location->start() : location->end());
4058                         }
4059                 }
4060         }
4061 }
4062
4063 void
4064 MarkerDrag::update_item (Location*)
4065 {
4066         /* noop */
4067 }
4068
4069 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4070         : Drag (e, i),
4071           _cumulative_x_drag (0)
4072         , _cumulative_y_drag (0)
4073         , _pushing (false)
4074 {
4075         if (_zero_gain_fraction < 0.0) {
4076                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4077         }
4078
4079         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4080
4081         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4082         assert (_point);
4083 }
4084
4085
4086 void
4087 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4088 {
4089         Drag::start_grab (event, _editor->cursors()->fader);
4090
4091         // start the grab at the center of the control point so
4092         // the point doesn't 'jump' to the mouse after the first drag
4093         _fixed_grab_x = _point->get_x();
4094         _fixed_grab_y = _point->get_y();
4095
4096         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4097         setup_snap_delta (pos);
4098
4099         float const fraction = 1 - (_point->get_y() / _point->line().height());
4100         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4101
4102         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4103
4104         if (!_point->can_slide ()) {
4105                 _x_constrained = true;
4106         }
4107 }
4108
4109 void
4110 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4111 {
4112         double dx = _drags->current_pointer_x() - last_pointer_x();
4113         double dy = current_pointer_y() - last_pointer_y();
4114
4115         if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4116                 dx *= 0.1;
4117                 dy *= 0.1;
4118         }
4119
4120         /* coordinate in pixels relative to the start of the region (for region-based automation)
4121            or track (for track-based automation) */
4122         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4123         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4124
4125         // calculate zero crossing point. back off by .01 to stay on the
4126         // positive side of zero
4127         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4128
4129         if (_x_constrained) {
4130                 cx = _fixed_grab_x;
4131         }
4132         if (_y_constrained) {
4133                 cy = _fixed_grab_y;
4134         }
4135
4136         _cumulative_x_drag = cx - _fixed_grab_x;
4137         _cumulative_y_drag = cy - _fixed_grab_y;
4138
4139         // make sure we hit zero when passing through
4140         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4141                 cy = zero_gain_y;
4142         }
4143
4144         cx = max (0.0, cx);
4145         cy = max (0.0, cy);
4146         cy = min ((double) _point->line().height(), cy);
4147
4148         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4149
4150         if (!_x_constrained) {
4151                 _editor->snap_to_with_modifier (cx_frames, event);
4152         }
4153
4154         cx_frames -= snap_delta (event->button.state);
4155         cx_frames = min (cx_frames, _point->line().maximum_time());
4156
4157         float const fraction = 1.0 - (cy / _point->line().height());
4158
4159         if (first_motion) {
4160                 _editor->begin_reversible_command (_("automation event move"));
4161                 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4162         }
4163
4164         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4165
4166         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4167 }
4168
4169 void
4170 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4171 {
4172         if (!movement_occurred) {
4173
4174                 /* just a click */
4175                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4176                         _editor->reset_point_selection ();
4177                 }
4178
4179         } else {
4180                 motion (event, false);
4181                 _point->line().end_drag (_pushing, _final_index);
4182                 _editor->commit_reversible_command ();
4183         }
4184 }
4185
4186 void
4187 ControlPointDrag::aborted (bool)
4188 {
4189         _point->line().reset ();
4190 }
4191
4192 bool
4193 ControlPointDrag::active (Editing::MouseMode m)
4194 {
4195         if (m == Editing::MouseDraw) {
4196                 /* always active in mouse draw */
4197                 return true;
4198         }
4199
4200         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4201         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4202 }
4203
4204 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4205         : Drag (e, i)
4206         , _line (0)
4207         , _cumulative_y_drag (0)
4208         , _before (0)
4209         , _after (0)
4210 {
4211         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4212 }
4213
4214 void
4215 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4216 {
4217         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4218         assert (_line);
4219
4220         _item = &_line->grab_item ();
4221
4222         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4223            origin, and ditto for y.
4224         */
4225
4226         double cx = event->button.x;
4227         double cy = event->button.y;
4228
4229         _line->parent_group().canvas_to_item (cx, cy);
4230
4231         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4232
4233         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4234                 /* no adjacent points */
4235                 return;
4236         }
4237
4238         Drag::start_grab (event, _editor->cursors()->fader);
4239
4240         /* store grab start in parent frame */
4241
4242         _fixed_grab_x = cx;
4243         _fixed_grab_y = cy;
4244
4245         double fraction = 1.0 - (cy / _line->height());
4246
4247         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4248 }
4249
4250 void
4251 LineDrag::motion (GdkEvent* event, bool first_move)
4252 {
4253         double dy = current_pointer_y() - last_pointer_y();
4254
4255         if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4256                 dy *= 0.1;
4257         }
4258
4259         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4260
4261         _cumulative_y_drag = cy - _fixed_grab_y;
4262
4263         cy = max (0.0, cy);
4264         cy = min ((double) _line->height(), cy);
4265
4266         double const fraction = 1.0 - (cy / _line->height());
4267         uint32_t ignored;
4268
4269         if (first_move) {
4270                 _editor->begin_reversible_command (_("automation range move"));
4271                 _line->start_drag_line (_before, _after, fraction);
4272         }
4273
4274         /* we are ignoring x position for this drag, so we can just pass in anything */
4275         _line->drag_motion (0, fraction, true, false, ignored);
4276
4277         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4278 }
4279
4280 void
4281 LineDrag::finished (GdkEvent* event, bool movement_occured)
4282 {
4283         if (movement_occured) {
4284                 motion (event, false);
4285                 _line->end_drag (false, 0);
4286                 _editor->commit_reversible_command ();
4287         } else {
4288                 /* add a new control point on the line */
4289
4290                 AutomationTimeAxisView* atv;
4291
4292                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4293                         framepos_t where = _editor->canvas_event_sample (event, 0, 0);
4294
4295                         atv->add_automation_event (event, where, event->button.y, false);
4296                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4297                         AudioRegionView* arv;
4298
4299                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4300                                 arv->add_gain_point_event (arv->get_canvas_group (), event, false);
4301                         }
4302                 }
4303         }
4304 }
4305
4306 void
4307 LineDrag::aborted (bool)
4308 {
4309         _line->reset ();
4310 }
4311
4312 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4313         : Drag (e, i),
4314           _line (0),
4315           _cumulative_x_drag (0)
4316 {
4317         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4318 }
4319
4320 void
4321 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4322 {
4323         Drag::start_grab (event);
4324
4325         _line = reinterpret_cast<Line*> (_item);
4326         assert (_line);
4327
4328         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4329
4330         double cx = event->button.x;
4331         double cy = event->button.y;
4332
4333         _item->parent()->canvas_to_item (cx, cy);
4334
4335         /* store grab start in parent frame */
4336         _region_view_grab_x = cx;
4337
4338         _before = *(float*) _item->get_data ("position");
4339
4340         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4341
4342         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4343 }
4344
4345 void
4346 FeatureLineDrag::motion (GdkEvent*, bool)
4347 {
4348         double dx = _drags->current_pointer_x() - last_pointer_x();
4349
4350         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4351
4352         _cumulative_x_drag += dx;
4353
4354         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4355
4356         if (cx > _max_x){
4357                 cx = _max_x;
4358         }
4359         else if(cx < 0){
4360                 cx = 0;
4361         }
4362
4363         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4364         assert (bbox);
4365         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4366
4367         float *pos = new float;
4368         *pos = cx;
4369
4370         _line->set_data ("position", pos);
4371
4372         _before = cx;
4373 }
4374
4375 void
4376 FeatureLineDrag::finished (GdkEvent*, bool)
4377 {
4378         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4379         _arv->update_transient(_before, _before);
4380 }
4381
4382 void
4383 FeatureLineDrag::aborted (bool)
4384 {
4385         //_line->reset ();
4386 }
4387
4388 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4389         : Drag (e, i)
4390         , _vertical_only (false)
4391 {
4392         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4393 }
4394
4395 void
4396 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4397 {
4398         Drag::start_grab (event);
4399         show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4400 }
4401
4402 void
4403 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4404 {
4405         framepos_t start;
4406         framepos_t end;
4407         double y1;
4408         double y2;
4409
4410         framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4411
4412         framepos_t grab = grab_frame ();
4413         if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4414                 _editor->snap_to_with_modifier (grab, event);
4415         } else {
4416                 grab = raw_grab_frame ();
4417         }
4418
4419         /* base start and end on initial click position */
4420
4421         if (pf < grab) {
4422                 start = pf;
4423                 end = grab;
4424         } else {
4425                 end = pf;
4426                 start = grab;
4427         }
4428
4429         if (current_pointer_y() < grab_y()) {
4430                 y1 = current_pointer_y();
4431                 y2 = grab_y();
4432         } else {
4433                 y2 = current_pointer_y();
4434                 y1 = grab_y();
4435         }
4436
4437         if (start != end || y1 != y2) {
4438
4439                 double x1 = _editor->sample_to_pixel (start);
4440                 double x2 = _editor->sample_to_pixel (end);
4441                 const double min_dimension = 2.0;
4442
4443                 if (_vertical_only) {
4444                         /* fixed 10 pixel width */
4445                         x2 = x1 + 10;
4446                 } else {
4447                         if (x2 < x1) {
4448                                 x2 = min (x1 - min_dimension, x2);
4449                         } else {
4450                                 x2 = max (x1 + min_dimension, x2);
4451                         }
4452                 }
4453
4454                 if (y2 < y1) {
4455                         y2 = min (y1 - min_dimension, y2);
4456                 } else {
4457                         y2 = max (y1 + min_dimension, y2);
4458                 }
4459
4460                 /* translate rect into item space and set */
4461
4462                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4463
4464                 /* this drag is a _trackview_only == true drag, so the y1 and
4465                  * y2 (computed using current_pointer_y() and grab_y()) will be
4466                  * relative to the top of the trackview group). The
4467                  * rubberband rect has the same parent/scroll offset as the
4468                  * the trackview group, so we can use the "r" rect directly
4469                  * to set the shape of the rubberband.
4470                  */
4471
4472                 _editor->rubberband_rect->set (r);
4473                 _editor->rubberband_rect->show();
4474                 _editor->rubberband_rect->raise_to_top();
4475
4476                 show_verbose_cursor_time (pf);
4477
4478                 do_select_things (event, true);
4479         }
4480 }
4481
4482 void
4483 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4484 {
4485         framepos_t x1;
4486         framepos_t x2;
4487         framepos_t grab = grab_frame ();
4488         framepos_t lpf = last_pointer_frame ();
4489
4490         if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4491                 grab = raw_grab_frame ();
4492                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4493         }
4494
4495         if (grab < lpf) {
4496                 x1 = grab;
4497                 x2 = lpf;
4498         } else {
4499                 x2 = grab;
4500                 x1 = lpf;
4501         }
4502
4503         double y1;
4504         double y2;
4505
4506         if (current_pointer_y() < grab_y()) {
4507                 y1 = current_pointer_y();
4508                 y2 = grab_y();
4509         } else {
4510                 y2 = current_pointer_y();
4511                 y1 = grab_y();
4512         }
4513
4514         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4515 }
4516
4517 void
4518 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4519 {
4520         if (movement_occurred) {
4521
4522                 motion (event, false);
4523                 do_select_things (event, false);
4524
4525         } else {
4526
4527                 /* just a click */
4528
4529                 bool do_deselect = true;
4530                 MidiTimeAxisView* mtv;
4531
4532                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4533                         /* MIDI track */
4534                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4535                                 /* nothing selected */
4536                                 add_midi_region (mtv);
4537                                 do_deselect = false;
4538                         }
4539                 }
4540
4541                 /* do not deselect if Primary or Tertiary (toggle-select or
4542                  * extend-select are pressed.
4543                  */
4544
4545                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4546                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4547                     do_deselect) {
4548                         deselect_things ();
4549                 }
4550
4551         }
4552
4553         _editor->rubberband_rect->hide();
4554 }
4555
4556 void
4557 RubberbandSelectDrag::aborted (bool)
4558 {
4559         _editor->rubberband_rect->hide ();
4560 }
4561
4562 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4563         : RegionDrag (e, i, p, v)
4564 {
4565         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4566 }
4567
4568 void
4569 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4570 {
4571         Drag::start_grab (event, cursor);
4572
4573         _editor->get_selection().add (_primary);
4574
4575         framepos_t where = _primary->region()->position();
4576         setup_snap_delta (where);
4577
4578         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4579 }
4580
4581 void
4582 TimeFXDrag::motion (GdkEvent* event, bool)
4583 {
4584         RegionView* rv = _primary;
4585         StreamView* cv = rv->get_time_axis_view().view ();
4586
4587         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4588         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4589         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4590         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4591         _editor->snap_to_with_modifier (pf, event);
4592         pf -= snap_delta (event->button.state);
4593
4594         if (pf > rv->region()->position()) {
4595                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4596         }
4597
4598         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4599 }
4600
4601 void
4602 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4603 {
4604         _primary->get_time_axis_view().hide_timestretch ();
4605
4606         if (!movement_occurred) {
4607                 return;
4608         }
4609
4610         if (last_pointer_frame() < _primary->region()->position()) {
4611                 /* backwards drag of the left edge - not usable */
4612                 return;
4613         }
4614
4615         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4616
4617         float percentage = (double) newlen / (double) _primary->region()->length();
4618
4619 #ifndef USE_RUBBERBAND
4620         // Soundtouch uses percentage / 100 instead of normal (/ 1)
4621         if (_primary->region()->data_type() == DataType::AUDIO) {
4622                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4623         }
4624 #endif
4625
4626         if (!_editor->get_selection().regions.empty()) {
4627                 /* primary will already be included in the selection, and edit
4628                    group shared editing will propagate selection across
4629                    equivalent regions, so just use the current region
4630                    selection.
4631                 */
4632
4633                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4634                         error << _("An error occurred while executing time stretch operation") << endmsg;
4635                 }
4636         }
4637 }
4638
4639 void
4640 TimeFXDrag::aborted (bool)
4641 {
4642         _primary->get_time_axis_view().hide_timestretch ();
4643 }
4644
4645 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4646         : Drag (e, i)
4647 {
4648         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4649 }
4650
4651 void
4652 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4653 {
4654         Drag::start_grab (event);
4655 }
4656
4657 void
4658 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4659 {
4660         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4661 }
4662
4663 void
4664 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4665 {
4666         if (movement_occurred && _editor->session()) {
4667                 /* make sure we stop */
4668                 _editor->session()->request_transport_speed (0.0);
4669         }
4670 }
4671
4672 void
4673 ScrubDrag::aborted (bool)
4674 {
4675         /* XXX: TODO */
4676 }
4677
4678 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4679         : Drag (e, i)
4680         , _operation (o)
4681         , _add (false)
4682         , _time_selection_at_start (!_editor->get_selection().time.empty())
4683 {
4684         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4685
4686         if (_time_selection_at_start) {
4687                 start_at_start = _editor->get_selection().time.start();
4688                 end_at_start = _editor->get_selection().time.end_frame();
4689         }
4690 }
4691
4692 void
4693 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4694 {
4695         if (_editor->session() == 0) {
4696                 return;
4697         }
4698
4699         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4700
4701         switch (_operation) {
4702         case CreateSelection:
4703                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4704                         _add = true;
4705                 } else {
4706                         _add = false;
4707                 }
4708                 cursor = _editor->cursors()->selector;
4709                 Drag::start_grab (event, cursor);
4710                 break;
4711
4712         case SelectionStartTrim:
4713                 if (_editor->clicked_axisview) {
4714                         _editor->clicked_axisview->order_selection_trims (_item, true);
4715                 }
4716                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4717                 break;
4718
4719         case SelectionEndTrim:
4720                 if (_editor->clicked_axisview) {
4721                         _editor->clicked_axisview->order_selection_trims (_item, false);
4722                 }
4723                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4724                 break;
4725
4726         case SelectionMove:
4727                 Drag::start_grab (event, cursor);
4728                 break;
4729
4730         case SelectionExtend:
4731                 Drag::start_grab (event, cursor);
4732                 break;
4733         }
4734
4735         if (_operation == SelectionMove) {
4736                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4737         } else {
4738                 show_verbose_cursor_time (adjusted_current_frame (event));
4739         }
4740 }
4741
4742 void
4743 SelectionDrag::setup_pointer_frame_offset ()
4744 {
4745         switch (_operation) {
4746         case CreateSelection:
4747                 _pointer_frame_offset = 0;
4748                 break;
4749
4750         case SelectionStartTrim:
4751         case SelectionMove:
4752                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4753                 break;
4754
4755         case SelectionEndTrim:
4756                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4757                 break;
4758
4759         case SelectionExtend:
4760                 break;
4761         }
4762 }
4763
4764 void
4765 SelectionDrag::motion (GdkEvent* event, bool first_move)
4766 {
4767         framepos_t start = 0;
4768         framepos_t end = 0;
4769         framecnt_t length = 0;
4770         framecnt_t distance = 0;
4771
4772         framepos_t const pending_position = adjusted_current_frame (event);
4773
4774         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4775                 return;
4776         }
4777
4778         switch (_operation) {
4779         case CreateSelection:
4780         {
4781                 framepos_t grab = grab_frame ();
4782
4783                 if (first_move) {
4784                         grab = adjusted_current_frame (event, false);
4785                         if (grab < pending_position) {
4786                                 _editor->snap_to (grab, RoundDownMaybe);
4787                         }  else {
4788                                 _editor->snap_to (grab, RoundUpMaybe);
4789                         }
4790                 }
4791
4792                 if (pending_position < grab) {
4793                         start = pending_position;
4794                         end = grab;
4795                 } else {
4796                         end = pending_position;
4797                         start = grab;
4798                 }
4799
4800                 /* first drag: Either add to the selection
4801                    or create a new selection
4802                 */
4803
4804                 if (first_move) {
4805
4806                         if (_add) {
4807
4808                                 /* adding to the selection */
4809                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4810                                 _editor->clicked_selection = _editor->selection->add (start, end);
4811                                 _add = false;
4812
4813                         } else {
4814
4815                                 /* new selection */
4816
4817                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4818                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4819                                 }
4820
4821                                 _editor->clicked_selection = _editor->selection->set (start, end);
4822                         }
4823                 }
4824
4825                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4826                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4827                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4828                 if (atest) {
4829                         _editor->selection->add (atest);
4830                         break;
4831                 }
4832
4833                 /* select all tracks within the rectangle that we've marked out so far */
4834                 TrackViewList new_selection;
4835                 TrackViewList& all_tracks (_editor->track_views);
4836
4837                 ArdourCanvas::Coord const top = grab_y();
4838                 ArdourCanvas::Coord const bottom = current_pointer_y();
4839
4840                 if (top >= 0 && bottom >= 0) {
4841
4842                         //first, find the tracks that are covered in the y range selection
4843                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4844                                 if ((*i)->covered_by_y_range (top, bottom)) {
4845                                         new_selection.push_back (*i);
4846                                 }
4847                         }
4848
4849                         //now find any tracks that are GROUPED with the tracks we selected
4850                         TrackViewList grouped_add = new_selection;
4851                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4852                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4853                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4854                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4855                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4856                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4857                                                         grouped_add.push_back (*j);
4858                                         }
4859                                 }
4860                         }
4861
4862                         //now compare our list with the current selection, and add or remove as necessary
4863                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4864                         TrackViewList tracks_to_add;
4865                         TrackViewList tracks_to_remove;
4866                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4867                                 if ( !_editor->selection->tracks.contains ( *i ) )
4868                                         tracks_to_add.push_back ( *i );
4869                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4870                                 if ( !grouped_add.contains ( *i ) )
4871                                         tracks_to_remove.push_back ( *i );
4872                         _editor->selection->add(tracks_to_add);
4873                         _editor->selection->remove(tracks_to_remove);
4874
4875                 }
4876         }
4877         break;
4878
4879         case SelectionStartTrim:
4880
4881                 end = _editor->selection->time[_editor->clicked_selection].end;
4882
4883                 if (pending_position > end) {
4884                         start = end;
4885                 } else {
4886                         start = pending_position;
4887                 }
4888                 break;
4889
4890         case SelectionEndTrim:
4891
4892                 start = _editor->selection->time[_editor->clicked_selection].start;
4893
4894                 if (pending_position < start) {
4895                         end = start;
4896                 } else {
4897                         end = pending_position;
4898                 }
4899
4900                 break;
4901
4902         case SelectionMove:
4903
4904                 start = _editor->selection->time[_editor->clicked_selection].start;
4905                 end = _editor->selection->time[_editor->clicked_selection].end;
4906
4907                 length = end - start;
4908                 distance = pending_position - start;
4909                 start = pending_position;
4910                 _editor->snap_to (start);
4911
4912                 end = start + length;
4913
4914                 break;
4915
4916         case SelectionExtend:
4917                 break;
4918         }
4919
4920         if (start != end) {
4921                 switch (_operation) {
4922                 case SelectionMove:
4923                         if (_time_selection_at_start) {
4924                                 _editor->selection->move_time (distance);
4925                         }
4926                         break;
4927                 default:
4928                         _editor->selection->replace (_editor->clicked_selection, start, end);
4929                 }
4930         }
4931
4932         if (_operation == SelectionMove) {
4933                 show_verbose_cursor_time(start);
4934         } else {
4935                 show_verbose_cursor_time(pending_position);
4936         }
4937 }
4938
4939 void
4940 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4941 {
4942         Session* s = _editor->session();
4943
4944         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4945         if (movement_occurred) {
4946                 motion (event, false);
4947                 /* XXX this is not object-oriented programming at all. ick */
4948                 if (_editor->selection->time.consolidate()) {
4949                         _editor->selection->TimeChanged ();
4950                 }
4951
4952                 /* XXX what if its a music time selection? */
4953                 if (s) {
4954                         if (s->get_play_range() && s->transport_rolling()) {
4955                                 s->request_play_range (&_editor->selection->time, true);
4956                         } else {
4957                                 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4958                                         if (_operation == SelectionEndTrim)
4959                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4960                                         else
4961                                                 s->request_locate (_editor->get_selection().time.start());
4962                                 }
4963                         }
4964
4965                         if (_editor->get_selection().time.length() != 0) {
4966                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
4967                         } else {
4968                                 s->clear_range_selection ();
4969                         }
4970                 }
4971                 
4972         } else {
4973                 /* just a click, no pointer movement.
4974                  */
4975
4976                 if (_operation == SelectionExtend) {
4977                         if (_time_selection_at_start) {
4978                                 framepos_t pos = adjusted_current_frame (event, false);
4979                                 framepos_t start = min (pos, start_at_start);
4980                                 framepos_t end = max (pos, end_at_start);
4981                                 _editor->selection->set (start, end);
4982                         }
4983                 } else {
4984                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4985                                 if (_editor->clicked_selection) {
4986                                         _editor->selection->remove (_editor->clicked_selection);
4987                                 }
4988                         } else {
4989                                 if (!_editor->clicked_selection) {
4990                                         _editor->selection->clear_time();
4991                                 }
4992                         }
4993                 }
4994
4995                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4996                         _editor->selection->set (_editor->clicked_axisview);
4997                 }
4998
4999                 if (s && s->get_play_range () && s->transport_rolling()) {
5000                         s->request_stop (false, false);
5001                 }
5002
5003         }
5004
5005         _editor->stop_canvas_autoscroll ();
5006         _editor->clicked_selection = 0;
5007         _editor->commit_reversible_selection_op ();
5008 }
5009
5010 void
5011 SelectionDrag::aborted (bool)
5012 {
5013         /* XXX: TODO */
5014 }
5015
5016 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5017         : Drag (e, i, false),
5018           _operation (o),
5019           _copy (false)
5020 {
5021         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5022
5023         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5024                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5025                                                                       physical_screen_height (_editor->get_window())));
5026         _drag_rect->hide ();
5027
5028         _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
5029         _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
5030 }
5031
5032 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5033 {
5034         /* normal canvas items will be cleaned up when their parent group is deleted. But
5035            this item is created as the child of a long-lived parent group, and so we
5036            need to explicitly delete it.
5037         */
5038         delete _drag_rect;
5039 }
5040
5041 void
5042 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5043 {
5044         if (_editor->session() == 0) {
5045                 return;
5046         }
5047
5048         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5049
5050         if (!_editor->temp_location) {
5051                 _editor->temp_location = new Location (*_editor->session());
5052         }
5053
5054         switch (_operation) {
5055         case CreateSkipMarker:
5056         case CreateRangeMarker:
5057         case CreateTransportMarker:
5058         case CreateCDMarker:
5059
5060                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5061                         _copy = true;
5062                 } else {
5063                         _copy = false;
5064                 }
5065                 cursor = _editor->cursors()->selector;
5066                 break;
5067         }
5068
5069         Drag::start_grab (event, cursor);
5070
5071         show_verbose_cursor_time (adjusted_current_frame (event));
5072 }
5073
5074 void
5075 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5076 {
5077         framepos_t start = 0;
5078         framepos_t end = 0;
5079         ArdourCanvas::Rectangle *crect;
5080
5081         switch (_operation) {
5082         case CreateSkipMarker:
5083                 crect = _editor->range_bar_drag_rect;
5084                 break;
5085         case CreateRangeMarker:
5086                 crect = _editor->range_bar_drag_rect;
5087                 break;
5088         case CreateTransportMarker:
5089                 crect = _editor->transport_bar_drag_rect;
5090                 break;
5091         case CreateCDMarker:
5092                 crect = _editor->cd_marker_bar_drag_rect;
5093                 break;
5094         default:
5095                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5096                 return;
5097                 break;
5098         }
5099
5100         framepos_t const pf = adjusted_current_frame (event);
5101
5102         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5103                 framepos_t grab = grab_frame ();
5104                 _editor->snap_to (grab);
5105
5106                 if (pf < grab_frame()) {
5107                         start = pf;
5108                         end = grab;
5109                 } else {
5110                         end = pf;
5111                         start = grab;
5112                 }
5113
5114                 /* first drag: Either add to the selection
5115                    or create a new selection.
5116                 */
5117
5118                 if (first_move) {
5119
5120                         _editor->temp_location->set (start, end);
5121
5122                         crect->show ();
5123
5124                         update_item (_editor->temp_location);
5125                         _drag_rect->show();
5126                         //_drag_rect->raise_to_top();
5127
5128                 }
5129         }
5130
5131         if (start != end) {
5132                 _editor->temp_location->set (start, end);
5133
5134                 double x1 = _editor->sample_to_pixel (start);
5135                 double x2 = _editor->sample_to_pixel (end);
5136                 crect->set_x0 (x1);
5137                 crect->set_x1 (x2);
5138
5139                 update_item (_editor->temp_location);
5140         }
5141
5142         show_verbose_cursor_time (pf);
5143
5144 }
5145
5146 void
5147 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5148 {
5149         Location * newloc = 0;
5150         string rangename;
5151         int flags;
5152
5153         if (movement_occurred) {
5154                 motion (event, false);
5155                 _drag_rect->hide();
5156
5157                 switch (_operation) {
5158                 case CreateSkipMarker:
5159                 case CreateRangeMarker:
5160                 case CreateCDMarker:
5161                     {
5162                         XMLNode &before = _editor->session()->locations()->get_state();
5163                         if (_operation == CreateSkipMarker) {
5164                                 _editor->begin_reversible_command (_("new skip marker"));
5165                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5166                                 flags = Location::IsRangeMarker | Location::IsSkip;
5167                                 _editor->range_bar_drag_rect->hide();
5168                         } else if (_operation == CreateCDMarker) {
5169                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5170                                 _editor->begin_reversible_command (_("new CD marker"));
5171                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5172                                 _editor->cd_marker_bar_drag_rect->hide();
5173                         } else {
5174                                 _editor->begin_reversible_command (_("new skip marker"));
5175                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5176                                 flags = Location::IsRangeMarker;
5177                                 _editor->range_bar_drag_rect->hide();
5178                         }
5179                         newloc = new Location (
5180                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5181                                 );
5182
5183                         _editor->session()->locations()->add (newloc, true);
5184                         XMLNode &after = _editor->session()->locations()->get_state();
5185                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5186                         _editor->commit_reversible_command ();
5187                         break;
5188                     }
5189
5190                 case CreateTransportMarker:
5191                         // popup menu to pick loop or punch
5192                         _editor->new_transport_marker_context_menu (&event->button, _item);
5193                         break;
5194                 }
5195
5196         } else {
5197
5198                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5199
5200                 if (_operation == CreateTransportMarker) {
5201
5202                         /* didn't drag, so just locate */
5203
5204                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5205
5206                 } else if (_operation == CreateCDMarker) {
5207
5208                         /* didn't drag, but mark is already created so do
5209                          * nothing */
5210
5211                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5212
5213                         framepos_t start;
5214                         framepos_t end;
5215
5216                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5217
5218                         if (end == max_framepos) {
5219                                 end = _editor->session()->current_end_frame ();
5220                         }
5221
5222                         if (start == max_framepos) {
5223                                 start = _editor->session()->current_start_frame ();
5224                         }
5225
5226                         switch (_editor->mouse_mode) {
5227                         case MouseObject:
5228                                 /* find the two markers on either side and then make the selection from it */
5229                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5230                                 break;
5231
5232                         case MouseRange:
5233                                 /* find the two markers on either side of the click and make the range out of it */
5234                                 _editor->selection->set (start, end);
5235                                 break;
5236
5237                         default:
5238                                 break;
5239                         }
5240                 }
5241         }
5242
5243         _editor->stop_canvas_autoscroll ();
5244 }
5245
5246 void
5247 RangeMarkerBarDrag::aborted (bool movement_occured)
5248 {
5249         if (movement_occured) {
5250                 _drag_rect->hide ();
5251         }
5252 }
5253
5254 void
5255 RangeMarkerBarDrag::update_item (Location* location)
5256 {
5257         double const x1 = _editor->sample_to_pixel (location->start());
5258         double const x2 = _editor->sample_to_pixel (location->end());
5259
5260         _drag_rect->set_x0 (x1);
5261         _drag_rect->set_x1 (x2);
5262 }
5263
5264 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5265         : Drag (e, i)
5266         , _cumulative_dx (0)
5267         , _cumulative_dy (0)
5268 {
5269         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5270
5271         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5272         assert (_primary);
5273         _region = &_primary->region_view ();
5274         _note_height = _region->midi_stream_view()->note_height ();
5275 }
5276
5277 void
5278 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5279 {
5280         Drag::start_grab (event);
5281         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5282
5283         if (!(_was_selected = _primary->selected())) {
5284
5285                 /* tertiary-click means extend selection - we'll do that on button release,
5286                    so don't add it here, because otherwise we make it hard to figure
5287                    out the "extend-to" range.
5288                 */
5289
5290                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5291
5292                 if (!extend) {
5293                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5294
5295                         if (add) {
5296                                 _region->note_selected (_primary, true);
5297                         } else {
5298                                 _region->unique_select (_primary);
5299                         }
5300
5301                         _editor->begin_reversible_selection_op(X_("Select Note Press"));
5302                         _editor->commit_reversible_selection_op();
5303                 }
5304         }
5305 }
5306
5307 /** @return Current total drag x change in frames */
5308 frameoffset_t
5309 NoteDrag::total_dx (const guint state) const
5310 {
5311         /* dx in frames */
5312         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5313
5314         /* primary note time */
5315         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5316
5317         /* new time of the primary note in session frames */
5318         frameoffset_t st = n + dx + snap_delta (state);
5319
5320         framepos_t const rp = _region->region()->position ();
5321
5322         /* prevent the note being dragged earlier than the region's position */
5323         st = max (st, rp);
5324
5325         /* possibly snap and return corresponding delta */
5326
5327         bool snap = true;
5328
5329         if (ArdourKeyboard::indicates_snap (state)) {
5330                 if (_editor->snap_mode () != SnapOff) {
5331                         snap = false;
5332                 }
5333         } else {
5334                 if (_editor->snap_mode () == SnapOff) {
5335                         snap = false;
5336                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5337                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5338                                 snap = true;
5339                         }
5340                 }
5341         }
5342
5343         frameoffset_t ret;
5344         if (snap) {
5345                 ret =  _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5346         } else {
5347                 ret = st - n - snap_delta (state);
5348         }
5349         return ret;
5350 }
5351
5352 /** @return Current total drag y change in note number */
5353 int8_t
5354 NoteDrag::total_dy () const
5355 {
5356         MidiStreamView* msv = _region->midi_stream_view ();
5357         double const y = _region->midi_view()->y_position ();
5358         /* new current note */
5359         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5360         /* clamp */
5361         n = max (msv->lowest_note(), n);
5362         n = min (msv->highest_note(), n);
5363         /* and work out delta */
5364         return n - msv->y_to_note (grab_y() - y);
5365 }
5366
5367 void
5368 NoteDrag::motion (GdkEvent * event, bool)
5369 {
5370         /* Total change in x and y since the start of the drag */
5371         frameoffset_t const dx = total_dx (event->button.state);
5372         int8_t const dy = total_dy ();
5373
5374         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5375         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5376         double const tdy = -dy * _note_height - _cumulative_dy;
5377
5378         if (tdx || tdy) {
5379                 _cumulative_dx += tdx;
5380                 _cumulative_dy += tdy;
5381
5382                 int8_t note_delta = total_dy();
5383
5384                 _region->move_selection (tdx, tdy, note_delta);
5385
5386                 /* the new note value may be the same as the old one, but we
5387                  * don't know what that means because the selection may have
5388                  * involved more than one note and we might be doing something
5389                  * odd with them. so show the note value anyway, always.
5390                  */
5391
5392                 char buf[12];
5393                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5394
5395                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5396                           (int) floor ((double)new_note));
5397
5398                 show_verbose_cursor_text (buf);
5399         }
5400 }
5401
5402 void
5403 NoteDrag::finished (GdkEvent* ev, bool moved)
5404 {
5405         if (!moved) {
5406                 /* no motion - select note */
5407
5408                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5409                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5410
5411                         bool changed = false;
5412
5413                         if (_was_selected) {
5414                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5415                                 if (add) {
5416                                         _region->note_deselected (_primary);
5417                                         changed = true;
5418                                 }
5419                         } else {
5420                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5421                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5422
5423                                 if (!extend && !add && _region->selection_size() > 1) {
5424                                         _region->unique_select (_primary);
5425                                         changed = true;
5426                                 } else if (extend) {
5427                                         _region->note_selected (_primary, true, true);
5428                                         changed = true;
5429                                 } else {
5430                                         /* it was added during button press */
5431                                 }
5432                         }
5433
5434                         if (changed) {
5435                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5436                                 _editor->commit_reversible_selection_op();
5437                         }
5438                 }
5439         } else {
5440                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5441         }
5442 }
5443
5444 void
5445 NoteDrag::aborted (bool)
5446 {
5447         /* XXX: TODO */
5448 }
5449
5450 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5451 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5452         : Drag (editor, atv->base_item ())
5453         , _ranges (r)
5454         , _y_origin (atv->y_position())
5455         , _nothing_to_drag (false)
5456 {
5457         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5458         setup (atv->lines ());
5459 }
5460
5461 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5462 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5463         : Drag (editor, rv->get_canvas_group ())
5464         , _ranges (r)
5465         , _y_origin (rv->get_time_axis_view().y_position())
5466         , _nothing_to_drag (false)
5467         , _integral (false)
5468 {
5469         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5470
5471         list<boost::shared_ptr<AutomationLine> > lines;
5472
5473         AudioRegionView*      audio_view;
5474         AutomationRegionView* automation_view;
5475         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5476                 lines.push_back (audio_view->get_gain_line ());
5477         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5478                 lines.push_back (automation_view->line ());
5479                 _integral = true;
5480         } else {
5481                 error << _("Automation range drag created for invalid region type") << endmsg;
5482         }
5483
5484         setup (lines);
5485 }
5486
5487 /** @param lines AutomationLines to drag.
5488  *  @param offset Offset from the session start to the points in the AutomationLines.
5489  */
5490 void
5491 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5492 {
5493         /* find the lines that overlap the ranges being dragged */
5494         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5495         while (i != lines.end ()) {
5496                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5497                 ++j;
5498
5499                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5500
5501                 /* check this range against all the AudioRanges that we are using */
5502                 list<AudioRange>::const_iterator k = _ranges.begin ();
5503                 while (k != _ranges.end()) {
5504                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5505                                 break;
5506                         }
5507                         ++k;
5508                 }
5509
5510                 /* add it to our list if it overlaps at all */
5511                 if (k != _ranges.end()) {
5512                         Line n;
5513                         n.line = *i;
5514                         n.state = 0;
5515                         n.range = r;
5516                         _lines.push_back (n);
5517                 }
5518
5519                 i = j;
5520         }
5521
5522         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5523 }
5524
5525 double
5526 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5527 {
5528         return 1.0 - ((global_y - _y_origin) / line->height());
5529 }
5530
5531 double
5532 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5533 {
5534         const double v = list->eval(x);
5535         return _integral ? rint(v) : v;
5536 }
5537
5538 void
5539 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5540 {
5541         Drag::start_grab (event, cursor);
5542
5543         /* Get line states before we start changing things */
5544         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5545                 i->state = &i->line->get_state ();
5546                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5547         }
5548
5549         if (_ranges.empty()) {
5550
5551                 /* No selected time ranges: drag all points */
5552                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5553                         uint32_t const N = i->line->npoints ();
5554                         for (uint32_t j = 0; j < N; ++j) {
5555                                 i->points.push_back (i->line->nth (j));
5556                         }
5557                 }
5558
5559         } else {
5560
5561                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5562
5563                         framecnt_t const half = (i->start + i->end) / 2;
5564
5565                         /* find the line that this audio range starts in */
5566                         list<Line>::iterator j = _lines.begin();
5567                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5568                                 ++j;
5569                         }
5570
5571                         if (j != _lines.end()) {
5572                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5573
5574                                 /* j is the line that this audio range starts in; fade into it;
5575                                    64 samples length plucked out of thin air.
5576                                 */
5577
5578                                 framepos_t a = i->start + 64;
5579                                 if (a > half) {
5580                                         a = half;
5581                                 }
5582
5583                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5584                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5585
5586                                 the_list->editor_add (p, value (the_list, p), false);
5587                                 the_list->editor_add (q, value (the_list, q), false);
5588                         }
5589
5590                         /* same thing for the end */
5591
5592                         j = _lines.begin();
5593                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5594                                 ++j;
5595                         }
5596
5597                         if (j != _lines.end()) {
5598                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5599
5600                                 /* j is the line that this audio range starts in; fade out of it;
5601                                    64 samples length plucked out of thin air.
5602                                 */
5603
5604                                 framepos_t b = i->end - 64;
5605                                 if (b < half) {
5606                                         b = half;
5607                                 }
5608
5609                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5610                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5611
5612                                 the_list->editor_add (p, value (the_list, p), false);
5613                                 the_list->editor_add (q, value (the_list, q), false);
5614                         }
5615                 }
5616
5617                 _nothing_to_drag = true;
5618
5619                 /* Find all the points that should be dragged and put them in the relevant
5620                    points lists in the Line structs.
5621                 */
5622
5623                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5624
5625                         uint32_t const N = i->line->npoints ();
5626                         for (uint32_t j = 0; j < N; ++j) {
5627
5628                                 /* here's a control point on this line */
5629                                 ControlPoint* p = i->line->nth (j);
5630                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5631
5632                                 /* see if it's inside a range */
5633                                 list<AudioRange>::const_iterator k = _ranges.begin ();
5634                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5635                                         ++k;
5636                                 }
5637
5638                                 if (k != _ranges.end()) {
5639                                         /* dragging this point */
5640                                         _nothing_to_drag = false;
5641                                         i->points.push_back (p);
5642                                 }
5643                         }
5644                 }
5645         }
5646
5647         if (_nothing_to_drag) {
5648                 return;
5649         }
5650 }
5651
5652 void
5653 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5654 {
5655         if (_nothing_to_drag) {
5656                 return;
5657         }
5658
5659         if (first_move) {
5660                 _editor->begin_reversible_command (_("automation range move"));
5661                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5662                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5663                 }
5664         }
5665
5666         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5667                 float const f = y_fraction (l->line, current_pointer_y());
5668                 /* we are ignoring x position for this drag, so we can just pass in anything */
5669                 uint32_t ignored;
5670                 l->line->drag_motion (0, f, true, false, ignored);
5671                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5672         }
5673 }
5674
5675 void
5676 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5677 {
5678         if (_nothing_to_drag || !motion_occurred) {
5679                 return;
5680         }
5681
5682         motion (event, false);
5683         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5684                 i->line->end_drag (false, 0);
5685         }
5686
5687         _editor->commit_reversible_command ();
5688 }
5689
5690 void
5691 AutomationRangeDrag::aborted (bool)
5692 {
5693         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5694                 i->line->reset ();
5695         }
5696 }
5697
5698 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5699         : view (v)
5700         , initial_time_axis_view (itav)
5701 {
5702         /* note that time_axis_view may be null if the regionview was created
5703          * as part of a copy operation.
5704          */
5705         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5706         layer = v->region()->layer ();
5707         initial_y = v->get_canvas_group()->position().y;
5708         initial_playlist = v->region()->playlist ();
5709         initial_position = v->region()->position ();
5710         initial_end = v->region()->position () + v->region()->length ();
5711 }
5712
5713 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5714         : Drag (e, i->canvas_item ())
5715         , _region_view (r)
5716         , _patch_change (i)
5717         , _cumulative_dx (0)
5718 {
5719         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5720                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5721                                                    grab_frame()));
5722 }
5723
5724 void
5725 PatchChangeDrag::motion (GdkEvent* ev, bool)
5726 {
5727         framepos_t f = adjusted_current_frame (ev);
5728         boost::shared_ptr<Region> r = _region_view->region ();
5729         f = max (f, r->position ());
5730         f = min (f, r->last_frame ());
5731
5732         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5733         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5734         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5735         _cumulative_dx = dxu;
5736 }
5737
5738 void
5739 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5740 {
5741         if (!movement_occurred) {
5742                 return;
5743         }
5744
5745         boost::shared_ptr<Region> r (_region_view->region ());
5746         framepos_t f = adjusted_current_frame (ev);
5747         f = max (f, r->position ());
5748         f = min (f, r->last_frame ());
5749
5750         _region_view->move_patch_change (
5751                 *_patch_change,
5752                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5753                 );
5754 }
5755
5756 void
5757 PatchChangeDrag::aborted (bool)
5758 {
5759         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5760 }
5761
5762 void
5763 PatchChangeDrag::setup_pointer_frame_offset ()
5764 {
5765         boost::shared_ptr<Region> region = _region_view->region ();
5766         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5767 }
5768
5769 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5770         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5771         , _region_view (rv)
5772 {
5773
5774 }
5775
5776 void
5777 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5778 {
5779         _region_view->update_drag_selection (
5780                 x1, x2, y1, y2,
5781                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5782 }
5783
5784 void
5785 MidiRubberbandSelectDrag::deselect_things ()
5786 {
5787         /* XXX */
5788 }
5789
5790 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5791         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5792         , _region_view (rv)
5793 {
5794         _vertical_only = true;
5795 }
5796
5797 void
5798 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5799 {
5800         double const y = _region_view->midi_view()->y_position ();
5801
5802         y1 = max (0.0, y1 - y);
5803         y2 = max (0.0, y2 - y);
5804
5805         _region_view->update_vertical_drag_selection (
5806                 y1,
5807                 y2,
5808                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5809                 );
5810 }
5811
5812 void
5813 MidiVerticalSelectDrag::deselect_things ()
5814 {
5815         /* XXX */
5816 }
5817
5818 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5819         : RubberbandSelectDrag (e, i)
5820 {
5821
5822 }
5823
5824 void
5825 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5826 {
5827         if (drag_in_progress) {
5828                 /* We just want to select things at the end of the drag, not during it */
5829                 return;
5830         }
5831
5832         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5833
5834         _editor->begin_reversible_selection_op (X_("rubberband selection"));
5835
5836         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5837
5838         _editor->commit_reversible_selection_op ();
5839 }
5840
5841 void
5842 EditorRubberbandSelectDrag::deselect_things ()
5843 {
5844         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5845
5846         _editor->selection->clear_tracks();
5847         _editor->selection->clear_regions();
5848         _editor->selection->clear_points ();
5849         _editor->selection->clear_lines ();
5850         _editor->selection->clear_midi_notes ();
5851
5852         _editor->commit_reversible_selection_op();
5853 }
5854
5855 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5856         : Drag (e, i)
5857         , _region_view (rv)
5858         , _drag_rect (0)
5859 {
5860         _note[0] = _note[1] = 0;
5861 }
5862
5863 NoteCreateDrag::~NoteCreateDrag ()
5864 {
5865         delete _drag_rect;
5866 }
5867
5868 framecnt_t
5869 NoteCreateDrag::grid_frames (framepos_t t) const
5870 {
5871         bool success;
5872         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5873         if (!success) {
5874                 grid_beats = Evoral::Beats(1);
5875         }
5876
5877         return _region_view->region_beats_to_region_frames (grid_beats);
5878 }
5879
5880 void
5881 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5882 {
5883         Drag::start_grab (event, cursor);
5884
5885         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5886
5887         framepos_t pf = _drags->current_pointer_frame ();
5888         framecnt_t const g = grid_frames (pf);
5889
5890         /* Hack so that we always snap to the note that we are over, instead of snapping
5891            to the next one if we're more than halfway through the one we're over.
5892         */
5893         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5894                 pf -= g / 2;
5895         }
5896
5897         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5898         _note[1] = _note[0];
5899
5900         MidiStreamView* sv = _region_view->midi_stream_view ();
5901         double const x = _editor->sample_to_pixel (_note[0]);
5902         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5903
5904         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5905         _drag_rect->set_outline_all ();
5906         _drag_rect->set_outline_color (0xffffff99);
5907         _drag_rect->set_fill_color (0xffffff66);
5908 }
5909
5910 void
5911 NoteCreateDrag::motion (GdkEvent* event, bool)
5912 {
5913         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5914         double const x0 = _editor->sample_to_pixel (_note[0]);
5915         double const x1 = _editor->sample_to_pixel (_note[1]);
5916         _drag_rect->set_x0 (std::min(x0, x1));
5917         _drag_rect->set_x1 (std::max(x0, x1));
5918 }
5919
5920 void
5921 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5922 {
5923         if (!had_movement) {
5924                 return;
5925         }
5926
5927         framepos_t const start = min (_note[0], _note[1]);
5928         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5929
5930         framecnt_t const g = grid_frames (start);
5931         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5932
5933         if (_editor->snap_mode() == SnapNormal && length < g) {
5934                 length = g;
5935         }
5936
5937         Evoral::Beats length_beats = max (
5938                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5939
5940         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5941 }
5942
5943 double
5944 NoteCreateDrag::y_to_region (double y) const
5945 {
5946         double x = 0;
5947         _region_view->get_canvas_group()->canvas_to_item (x, y);
5948         return y;
5949 }
5950
5951 void
5952 NoteCreateDrag::aborted (bool)
5953 {
5954
5955 }
5956
5957 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5958         : Drag (e, i)
5959         , arv (rv)
5960         , start (start_yn)
5961 {
5962         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5963 }
5964
5965 void
5966 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5967 {
5968         Drag::start_grab (event, cursor);
5969 }
5970
5971 void
5972 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5973 {
5974         double distance;
5975         double new_length;
5976         framecnt_t len;
5977
5978         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5979
5980         if (start) {
5981                 distance = _drags->current_pointer_x() - grab_x();
5982                 len = ar->fade_in()->back()->when;
5983         } else {
5984                 distance = grab_x() - _drags->current_pointer_x();
5985                 len = ar->fade_out()->back()->when;
5986         }
5987
5988         /* how long should it be ? */
5989
5990         new_length = len + _editor->pixel_to_sample (distance);
5991
5992         /* now check with the region that this is legal */
5993
5994         new_length = ar->verify_xfade_bounds (new_length, start);
5995
5996         if (start) {
5997                 arv->reset_fade_in_shape_width (ar, new_length);
5998         } else {
5999                 arv->reset_fade_out_shape_width (ar, new_length);
6000         }
6001 }
6002
6003 void
6004 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6005 {
6006         double distance;
6007         double new_length;
6008         framecnt_t len;
6009
6010         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6011
6012         if (start) {
6013                 distance = _drags->current_pointer_x() - grab_x();
6014                 len = ar->fade_in()->back()->when;
6015         } else {
6016                 distance = grab_x() - _drags->current_pointer_x();
6017                 len = ar->fade_out()->back()->when;
6018         }
6019
6020         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6021
6022         _editor->begin_reversible_command ("xfade trim");
6023         ar->playlist()->clear_owned_changes ();
6024
6025         if (start) {
6026                 ar->set_fade_in_length (new_length);
6027         } else {
6028                 ar->set_fade_out_length (new_length);
6029         }
6030
6031         /* Adjusting the xfade may affect other regions in the playlist, so we need
6032            to get undo Commands from the whole playlist rather than just the
6033            region.
6034         */
6035
6036         vector<Command*> cmds;
6037         ar->playlist()->rdiff (cmds);
6038         _editor->session()->add_commands (cmds);
6039         _editor->commit_reversible_command ();
6040
6041 }
6042
6043 void
6044 CrossfadeEdgeDrag::aborted (bool)
6045 {
6046         if (start) {
6047                 // arv->redraw_start_xfade ();
6048         } else {
6049                 // arv->redraw_end_xfade ();
6050         }
6051 }
6052
6053 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6054         : Drag (e, item, true)
6055         , line (new EditorCursor (*e))
6056 {
6057         line->set_position (pos);
6058         line->show ();
6059 }
6060
6061 RegionCutDrag::~RegionCutDrag ()
6062 {
6063         delete line;
6064 }
6065
6066 void
6067 RegionCutDrag::motion (GdkEvent*, bool)
6068 {
6069         framepos_t where = _drags->current_pointer_frame();
6070         _editor->snap_to (where);
6071
6072         line->set_position (where);
6073 }
6074
6075 void
6076 RegionCutDrag::finished (GdkEvent*, bool)
6077 {
6078         _editor->get_track_canvas()->canvas()->re_enter();
6079
6080         framepos_t pos = _drags->current_pointer_frame();
6081
6082         line->hide ();
6083
6084         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6085
6086         if (rs.empty()) {
6087                 return;
6088         }
6089
6090         _editor->split_regions_at (pos, rs);
6091 }
6092
6093 void
6094 RegionCutDrag::aborted (bool)
6095 {
6096 }