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