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