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