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