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