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