9f52dea5b6aa766481f75f21f6f4b604868efa25
[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         , _first_move (true)
4039 {
4040         if (_zero_gain_fraction < 0.0) {
4041                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4042         }
4043
4044         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4045
4046         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4047         assert (_point);
4048 }
4049
4050
4051 void
4052 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4053 {
4054         Drag::start_grab (event, _editor->cursors()->fader);
4055
4056         // start the grab at the center of the control point so
4057         // the point doesn't 'jump' to the mouse after the first drag
4058         _fixed_grab_x = _point->get_x();
4059         _fixed_grab_y = _point->get_y();
4060
4061         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4062         setup_snap_delta (pos);
4063
4064         float const fraction = 1 - (_point->get_y() / _point->line().height());
4065         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4066
4067         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4068
4069         if (!_point->can_slide ()) {
4070                 _x_constrained = true;
4071         }
4072 }
4073
4074 void
4075 ControlPointDrag::motion (GdkEvent* event, bool)
4076 {
4077         double dx = _drags->current_pointer_x() - last_pointer_x();
4078         double dy = current_pointer_y() - last_pointer_y();
4079
4080         if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4081                 dx *= 0.1;
4082                 dy *= 0.1;
4083         }
4084
4085         /* coordinate in pixels relative to the start of the region (for region-based automation)
4086            or track (for track-based automation) */
4087         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4088         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4089
4090         // calculate zero crossing point. back off by .01 to stay on the
4091         // positive side of zero
4092         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4093
4094         // make sure we hit zero when passing through
4095         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4096                 cy = zero_gain_y;
4097         }
4098
4099         if (_x_constrained) {
4100                 cx = _fixed_grab_x;
4101         }
4102         if (_y_constrained) {
4103                 cy = _fixed_grab_y;
4104         }
4105
4106         _cumulative_x_drag = cx - _fixed_grab_x;
4107         _cumulative_y_drag = cy - _fixed_grab_y;
4108
4109         cx = max (0.0, cx);
4110         cy = max (0.0, cy);
4111         cy = min ((double) _point->line().height(), cy);
4112
4113         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4114
4115         if (!_x_constrained) {
4116                 _editor->snap_to_with_modifier (cx_frames, event);
4117         }
4118
4119         cx_frames -= snap_delta (event->button.state);
4120         cx_frames = min (cx_frames, _point->line().maximum_time());
4121
4122         float const fraction = 1.0 - (cy / _point->line().height());
4123
4124         if (_first_move) {
4125                 _editor->begin_reversible_command (_("automation event move"));
4126                 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4127                 _first_move = false;
4128         }
4129
4130         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4131
4132         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4133 }
4134
4135 void
4136 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4137 {
4138         if (!movement_occurred) {
4139
4140                 /* just a click */
4141                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4142                         _editor->reset_point_selection ();
4143                 }
4144
4145         } else {
4146                 motion (event, false);
4147         }
4148
4149         if (!_first_move) {
4150                 _point->line().end_drag (_pushing, _final_index);
4151                 _editor->commit_reversible_command ();
4152         }
4153 }
4154
4155 void
4156 ControlPointDrag::aborted (bool)
4157 {
4158         _point->line().reset ();
4159 }
4160
4161 bool
4162 ControlPointDrag::active (Editing::MouseMode m)
4163 {
4164         if (m == Editing::MouseDraw) {
4165                 /* always active in mouse draw */
4166                 return true;
4167         }
4168
4169         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4170         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4171 }
4172
4173 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4174         : Drag (e, i)
4175         , _line (0)
4176         , _cumulative_y_drag (0)
4177 {
4178         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4179 }
4180
4181 void
4182 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4183 {
4184         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4185         assert (_line);
4186
4187         _item = &_line->grab_item ();
4188
4189         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4190            origin, and ditto for y.
4191         */
4192
4193         double cx = event->button.x;
4194         double cy = event->button.y;
4195
4196         _line->parent_group().canvas_to_item (cx, cy);
4197
4198         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4199
4200         uint32_t before;
4201         uint32_t after;
4202
4203         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
4204                 /* no adjacent points */
4205                 return;
4206         }
4207
4208         Drag::start_grab (event, _editor->cursors()->fader);
4209
4210         /* store grab start in parent frame */
4211
4212         _fixed_grab_x = cx;
4213         _fixed_grab_y = cy;
4214
4215         double fraction = 1.0 - (cy / _line->height());
4216         _editor->begin_reversible_command (_("automation range move"));
4217         _line->start_drag_line (before, after, fraction);
4218
4219         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4220 }
4221
4222 void
4223 LineDrag::motion (GdkEvent* event, bool)
4224 {
4225         double dy = current_pointer_y() - last_pointer_y();
4226
4227         if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4228                 dy *= 0.1;
4229         }
4230
4231         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4232
4233         _cumulative_y_drag = cy - _fixed_grab_y;
4234
4235         cy = max (0.0, cy);
4236         cy = min ((double) _line->height(), cy);
4237
4238         double const fraction = 1.0 - (cy / _line->height());
4239         uint32_t ignored;
4240
4241         /* we are ignoring x position for this drag, so we can just pass in anything */
4242         _line->drag_motion (0, fraction, true, false, ignored);
4243
4244         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4245 }
4246
4247 void
4248 LineDrag::finished (GdkEvent* event, bool movement_occured)
4249 {
4250         if (movement_occured) {
4251                 motion (event, false);
4252                 _line->end_drag (false, 0);
4253         } else {
4254                 /* add a new control point on the line */
4255
4256                 AutomationTimeAxisView* atv;
4257
4258                 _line->end_drag (false, 0);
4259
4260                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4261                         framepos_t where = _editor->window_event_sample (event, 0, 0);
4262                         atv->add_automation_event (event, where, event->button.y, false);
4263                 }
4264         }
4265
4266         _editor->commit_reversible_command ();
4267 }
4268
4269 void
4270 LineDrag::aborted (bool)
4271 {
4272         _line->reset ();
4273 }
4274
4275 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4276         : Drag (e, i),
4277           _line (0),
4278           _cumulative_x_drag (0)
4279 {
4280         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4281 }
4282
4283 void
4284 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4285 {
4286         Drag::start_grab (event);
4287
4288         _line = reinterpret_cast<Line*> (_item);
4289         assert (_line);
4290
4291         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4292
4293         double cx = event->button.x;
4294         double cy = event->button.y;
4295
4296         _item->parent()->canvas_to_item (cx, cy);
4297
4298         /* store grab start in parent frame */
4299         _region_view_grab_x = cx;
4300
4301         _before = *(float*) _item->get_data ("position");
4302
4303         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4304
4305         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4306 }
4307
4308 void
4309 FeatureLineDrag::motion (GdkEvent*, bool)
4310 {
4311         double dx = _drags->current_pointer_x() - last_pointer_x();
4312
4313         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4314
4315         _cumulative_x_drag += dx;
4316
4317         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4318
4319         if (cx > _max_x){
4320                 cx = _max_x;
4321         }
4322         else if(cx < 0){
4323                 cx = 0;
4324         }
4325
4326         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4327         assert (bbox);
4328         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4329
4330         float *pos = new float;
4331         *pos = cx;
4332
4333         _line->set_data ("position", pos);
4334
4335         _before = cx;
4336 }
4337
4338 void
4339 FeatureLineDrag::finished (GdkEvent*, bool)
4340 {
4341         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4342         _arv->update_transient(_before, _before);
4343 }
4344
4345 void
4346 FeatureLineDrag::aborted (bool)
4347 {
4348         //_line->reset ();
4349 }
4350
4351 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4352         : Drag (e, i)
4353         , _vertical_only (false)
4354 {
4355         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4356 }
4357
4358 void
4359 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4360 {
4361         Drag::start_grab (event);
4362         show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4363 }
4364
4365 void
4366 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4367 {
4368         framepos_t start;
4369         framepos_t end;
4370         double y1;
4371         double y2;
4372
4373         framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4374
4375         framepos_t grab = grab_frame ();
4376         if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4377                 _editor->snap_to_with_modifier (grab, event);
4378         } else {
4379                 grab = raw_grab_frame ();
4380         }
4381
4382         /* base start and end on initial click position */
4383
4384         if (pf < grab) {
4385                 start = pf;
4386                 end = grab;
4387         } else {
4388                 end = pf;
4389                 start = grab;
4390         }
4391
4392         if (current_pointer_y() < grab_y()) {
4393                 y1 = current_pointer_y();
4394                 y2 = grab_y();
4395         } else {
4396                 y2 = current_pointer_y();
4397                 y1 = grab_y();
4398         }
4399
4400         if (start != end || y1 != y2) {
4401
4402                 double x1 = _editor->sample_to_pixel (start);
4403                 double x2 = _editor->sample_to_pixel (end);
4404                 const double min_dimension = 2.0;
4405
4406                 if (_vertical_only) {
4407                         /* fixed 10 pixel width */
4408                         x2 = x1 + 10;
4409                 } else {
4410                         if (x2 < x1) {
4411                                 x2 = min (x1 - min_dimension, x2);
4412                         } else {
4413                                 x2 = max (x1 + min_dimension, x2);
4414                         }
4415                 }
4416
4417                 if (y2 < y1) {
4418                         y2 = min (y1 - min_dimension, y2);
4419                 } else {
4420                         y2 = max (y1 + min_dimension, y2);
4421                 }
4422
4423                 /* translate rect into item space and set */
4424
4425                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4426
4427                 /* this drag is a _trackview_only == true drag, so the y1 and
4428                  * y2 (computed using current_pointer_y() and grab_y()) will be
4429                  * relative to the top of the trackview group). The
4430                  * rubberband rect has the same parent/scroll offset as the
4431                  * the trackview group, so we can use the "r" rect directly
4432                  * to set the shape of the rubberband.
4433                  */
4434
4435                 _editor->rubberband_rect->set (r);
4436                 _editor->rubberband_rect->show();
4437                 _editor->rubberband_rect->raise_to_top();
4438
4439                 show_verbose_cursor_time (pf);
4440
4441                 do_select_things (event, true);
4442         }
4443 }
4444
4445 void
4446 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4447 {
4448         framepos_t x1;
4449         framepos_t x2;
4450         framepos_t grab = grab_frame ();
4451         framepos_t lpf = last_pointer_frame ();
4452
4453         if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4454                 grab = raw_grab_frame ();
4455                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4456         }
4457
4458         if (grab < lpf) {
4459                 x1 = grab;
4460                 x2 = lpf;
4461         } else {
4462                 x2 = grab;
4463                 x1 = lpf;
4464         }
4465
4466         double y1;
4467         double y2;
4468
4469         if (current_pointer_y() < grab_y()) {
4470                 y1 = current_pointer_y();
4471                 y2 = grab_y();
4472         } else {
4473                 y2 = current_pointer_y();
4474                 y1 = grab_y();
4475         }
4476
4477         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4478 }
4479
4480 void
4481 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4482 {
4483         if (movement_occurred) {
4484
4485                 motion (event, false);
4486                 do_select_things (event, false);
4487
4488         } else {
4489
4490                 /* just a click */
4491
4492                 bool do_deselect = true;
4493                 MidiTimeAxisView* mtv;
4494
4495                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4496                         /* MIDI track */
4497                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4498                                 /* nothing selected */
4499                                 add_midi_region (mtv);
4500                                 do_deselect = false;
4501                         }
4502                 }
4503
4504                 /* do not deselect if Primary or Tertiary (toggle-select or
4505                  * extend-select are pressed.
4506                  */
4507
4508                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4509                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4510                     do_deselect) {
4511                         deselect_things ();
4512                 }
4513
4514         }
4515
4516         _editor->rubberband_rect->hide();
4517 }
4518
4519 void
4520 RubberbandSelectDrag::aborted (bool)
4521 {
4522         _editor->rubberband_rect->hide ();
4523 }
4524
4525 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4526         : RegionDrag (e, i, p, v)
4527 {
4528         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4529 }
4530
4531 void
4532 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4533 {
4534         Drag::start_grab (event, cursor);
4535
4536         _editor->get_selection().add (_primary);
4537
4538         framepos_t where = _primary->region()->position();
4539         setup_snap_delta (where);
4540
4541         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4542 }
4543
4544 void
4545 TimeFXDrag::motion (GdkEvent* event, bool)
4546 {
4547         RegionView* rv = _primary;
4548         StreamView* cv = rv->get_time_axis_view().view ();
4549
4550         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4551         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4552         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4553         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4554         _editor->snap_to_with_modifier (pf, event);
4555         pf -= snap_delta (event->button.state);
4556
4557         if (pf > rv->region()->position()) {
4558                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4559         }
4560
4561         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4562 }
4563
4564 void
4565 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4566 {
4567         _primary->get_time_axis_view().hide_timestretch ();
4568
4569         if (!movement_occurred) {
4570                 return;
4571         }
4572
4573         if (last_pointer_frame() < _primary->region()->position()) {
4574                 /* backwards drag of the left edge - not usable */
4575                 return;
4576         }
4577
4578         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4579
4580         float percentage = (double) newlen / (double) _primary->region()->length();
4581
4582 #ifndef USE_RUBBERBAND
4583         // Soundtouch uses percentage / 100 instead of normal (/ 1)
4584         if (_primary->region()->data_type() == DataType::AUDIO) {
4585                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4586         }
4587 #endif
4588
4589         if (!_editor->get_selection().regions.empty()) {
4590                 /* primary will already be included in the selection, and edit
4591                    group shared editing will propagate selection across
4592                    equivalent regions, so just use the current region
4593                    selection.
4594                 */
4595
4596                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4597                         error << _("An error occurred while executing time stretch operation") << endmsg;
4598                 }
4599         }
4600 }
4601
4602 void
4603 TimeFXDrag::aborted (bool)
4604 {
4605         _primary->get_time_axis_view().hide_timestretch ();
4606 }
4607
4608 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4609         : Drag (e, i)
4610 {
4611         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4612 }
4613
4614 void
4615 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4616 {
4617         Drag::start_grab (event);
4618 }
4619
4620 void
4621 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4622 {
4623         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4624 }
4625
4626 void
4627 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4628 {
4629         if (movement_occurred && _editor->session()) {
4630                 /* make sure we stop */
4631                 _editor->session()->request_transport_speed (0.0);
4632         }
4633 }
4634
4635 void
4636 ScrubDrag::aborted (bool)
4637 {
4638         /* XXX: TODO */
4639 }
4640
4641 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4642         : Drag (e, i)
4643         , _operation (o)
4644         , _add (false)
4645         , _time_selection_at_start (!_editor->get_selection().time.empty())
4646 {
4647         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4648
4649         if (_time_selection_at_start) {
4650                 start_at_start = _editor->get_selection().time.start();
4651                 end_at_start = _editor->get_selection().time.end_frame();
4652         }
4653 }
4654
4655 void
4656 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4657 {
4658         if (_editor->session() == 0) {
4659                 return;
4660         }
4661
4662         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4663
4664         switch (_operation) {
4665         case CreateSelection:
4666                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4667                         _add = true;
4668                 } else {
4669                         _add = false;
4670                 }
4671                 cursor = _editor->cursors()->selector;
4672                 Drag::start_grab (event, cursor);
4673                 break;
4674
4675         case SelectionStartTrim:
4676                 if (_editor->clicked_axisview) {
4677                         _editor->clicked_axisview->order_selection_trims (_item, true);
4678                 }
4679                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4680                 break;
4681
4682         case SelectionEndTrim:
4683                 if (_editor->clicked_axisview) {
4684                         _editor->clicked_axisview->order_selection_trims (_item, false);
4685                 }
4686                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4687                 break;
4688
4689         case SelectionMove:
4690                 Drag::start_grab (event, cursor);
4691                 break;
4692
4693         case SelectionExtend:
4694                 Drag::start_grab (event, cursor);
4695                 break;
4696         }
4697
4698         if (_operation == SelectionMove) {
4699                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4700         } else {
4701                 show_verbose_cursor_time (adjusted_current_frame (event));
4702         }
4703 }
4704
4705 void
4706 SelectionDrag::setup_pointer_frame_offset ()
4707 {
4708         switch (_operation) {
4709         case CreateSelection:
4710                 _pointer_frame_offset = 0;
4711                 break;
4712
4713         case SelectionStartTrim:
4714         case SelectionMove:
4715                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4716                 break;
4717
4718         case SelectionEndTrim:
4719                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4720                 break;
4721
4722         case SelectionExtend:
4723                 break;
4724         }
4725 }
4726
4727 void
4728 SelectionDrag::motion (GdkEvent* event, bool first_move)
4729 {
4730         framepos_t start = 0;
4731         framepos_t end = 0;
4732         framecnt_t length = 0;
4733         framecnt_t distance = 0;
4734
4735         framepos_t const pending_position = adjusted_current_frame (event);
4736
4737         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4738                 return;
4739         }
4740
4741         switch (_operation) {
4742         case CreateSelection:
4743         {
4744                 framepos_t grab = grab_frame ();
4745
4746                 if (first_move) {
4747                         grab = adjusted_current_frame (event, false);
4748                         if (grab < pending_position) {
4749                                 _editor->snap_to (grab, RoundDownMaybe);
4750                         }  else {
4751                                 _editor->snap_to (grab, RoundUpMaybe);
4752                         }
4753                 }
4754
4755                 if (pending_position < grab) {
4756                         start = pending_position;
4757                         end = grab;
4758                 } else {
4759                         end = pending_position;
4760                         start = grab;
4761                 }
4762
4763                 /* first drag: Either add to the selection
4764                    or create a new selection
4765                 */
4766
4767                 if (first_move) {
4768
4769                         if (_add) {
4770
4771                                 /* adding to the selection */
4772                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4773                                 _editor->clicked_selection = _editor->selection->add (start, end);
4774                                 _add = false;
4775
4776                         } else {
4777
4778                                 /* new selection */
4779
4780                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4781                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4782                                 }
4783
4784                                 _editor->clicked_selection = _editor->selection->set (start, end);
4785                         }
4786                 }
4787
4788                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4789                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4790                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4791                 if (atest) {
4792                         _editor->selection->add (atest);
4793                         break;
4794                 }
4795
4796                 /* select all tracks within the rectangle that we've marked out so far */
4797                 TrackViewList new_selection;
4798                 TrackViewList& all_tracks (_editor->track_views);
4799
4800                 ArdourCanvas::Coord const top = grab_y();
4801                 ArdourCanvas::Coord const bottom = current_pointer_y();
4802
4803                 if (top >= 0 && bottom >= 0) {
4804
4805                         //first, find the tracks that are covered in the y range selection
4806                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4807                                 if ((*i)->covered_by_y_range (top, bottom)) {
4808                                         new_selection.push_back (*i);
4809                                 }
4810                         }
4811
4812                         //now find any tracks that are GROUPED with the tracks we selected
4813                         TrackViewList grouped_add = new_selection;
4814                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4815                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4816                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4817                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4818                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4819                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4820                                                         grouped_add.push_back (*j);
4821                                         }
4822                                 }
4823                         }
4824
4825                         //now compare our list with the current selection, and add or remove as necessary
4826                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4827                         TrackViewList tracks_to_add;
4828                         TrackViewList tracks_to_remove;
4829                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4830                                 if ( !_editor->selection->tracks.contains ( *i ) )
4831                                         tracks_to_add.push_back ( *i );
4832                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4833                                 if ( !grouped_add.contains ( *i ) )
4834                                         tracks_to_remove.push_back ( *i );
4835                         _editor->selection->add(tracks_to_add);
4836                         _editor->selection->remove(tracks_to_remove);
4837
4838                 }
4839         }
4840         break;
4841
4842         case SelectionStartTrim:
4843
4844                 start = _editor->selection->time[_editor->clicked_selection].start;
4845                 end = _editor->selection->time[_editor->clicked_selection].end;
4846
4847                 if (pending_position > end) {
4848                         start = end;
4849                 } else {
4850                         start = pending_position;
4851                 }
4852                 break;
4853
4854         case SelectionEndTrim:
4855
4856                 start = _editor->selection->time[_editor->clicked_selection].start;
4857                 end = _editor->selection->time[_editor->clicked_selection].end;
4858
4859                 if (pending_position < start) {
4860                         end = start;
4861                 } else {
4862                         end = pending_position;
4863                 }
4864
4865                 break;
4866
4867         case SelectionMove:
4868
4869                 start = _editor->selection->time[_editor->clicked_selection].start;
4870                 end = _editor->selection->time[_editor->clicked_selection].end;
4871
4872                 length = end - start;
4873                 distance = pending_position - start;
4874                 start = pending_position;
4875                 _editor->snap_to (start);
4876
4877                 end = start + length;
4878
4879                 break;
4880
4881         case SelectionExtend:
4882                 break;
4883         }
4884
4885         if (start != end) {
4886                 switch (_operation) {
4887                 case SelectionMove:
4888                         if (_time_selection_at_start) {
4889                                 _editor->selection->move_time (distance);
4890                         }
4891                         break;
4892                 default:
4893                         _editor->selection->replace (_editor->clicked_selection, start, end);
4894                 }
4895         }
4896
4897         if (_operation == SelectionMove) {
4898                 show_verbose_cursor_time(start);
4899         } else {
4900                 show_verbose_cursor_time(pending_position);
4901         }
4902 }
4903
4904 void
4905 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4906 {
4907         Session* s = _editor->session();
4908
4909         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4910         if (movement_occurred) {
4911                 motion (event, false);
4912                 /* XXX this is not object-oriented programming at all. ick */
4913                 if (_editor->selection->time.consolidate()) {
4914                         _editor->selection->TimeChanged ();
4915                 }
4916
4917                 /* XXX what if its a music time selection? */
4918                 if (s) {
4919                         if ( s->get_play_range() && s->transport_rolling() ) {
4920                                 s->request_play_range (&_editor->selection->time, true);
4921                         } else {
4922                                 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4923                                         if (_operation == SelectionEndTrim)
4924                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4925                                         else
4926                                                 s->request_locate (_editor->get_selection().time.start());
4927                                 }
4928                         }
4929                 }
4930
4931         } else {
4932                 /* just a click, no pointer movement.
4933                  */
4934
4935                 if (_operation == SelectionExtend) {
4936                         if (_time_selection_at_start) {
4937                                 framepos_t pos = adjusted_current_frame (event, false);
4938                                 framepos_t start = min (pos, start_at_start);
4939                                 framepos_t end = max (pos, end_at_start);
4940                                 _editor->selection->set (start, end);
4941                         }
4942                 } else {
4943                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4944                                 if (_editor->clicked_selection) {
4945                                         _editor->selection->remove (_editor->clicked_selection);
4946                                 }
4947                         } else {
4948                                 if (!_editor->clicked_selection) {
4949                                         _editor->selection->clear_time();
4950                                 }
4951                         }
4952                 }
4953
4954                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4955                         _editor->selection->set (_editor->clicked_axisview);
4956                 }
4957
4958                 if (s && s->get_play_range () && s->transport_rolling()) {
4959                         s->request_stop (false, false);
4960                 }
4961
4962         }
4963
4964         _editor->stop_canvas_autoscroll ();
4965         _editor->clicked_selection = 0;
4966         _editor->commit_reversible_selection_op ();
4967 }
4968
4969 void
4970 SelectionDrag::aborted (bool)
4971 {
4972         /* XXX: TODO */
4973 }
4974
4975 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4976         : Drag (e, i, false),
4977           _operation (o),
4978           _copy (false)
4979 {
4980         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4981
4982         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4983                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
4984                                                                       physical_screen_height (_editor->get_window())));
4985         _drag_rect->hide ();
4986
4987         _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4988         _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4989 }
4990
4991 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4992 {
4993         /* normal canvas items will be cleaned up when their parent group is deleted. But
4994            this item is created as the child of a long-lived parent group, and so we
4995            need to explicitly delete it.
4996         */
4997         delete _drag_rect;
4998 }
4999
5000 void
5001 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5002 {
5003         if (_editor->session() == 0) {
5004                 return;
5005         }
5006
5007         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5008
5009         if (!_editor->temp_location) {
5010                 _editor->temp_location = new Location (*_editor->session());
5011         }
5012
5013         switch (_operation) {
5014         case CreateSkipMarker:
5015         case CreateRangeMarker:
5016         case CreateTransportMarker:
5017         case CreateCDMarker:
5018
5019                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5020                         _copy = true;
5021                 } else {
5022                         _copy = false;
5023                 }
5024                 cursor = _editor->cursors()->selector;
5025                 break;
5026         }
5027
5028         Drag::start_grab (event, cursor);
5029
5030         show_verbose_cursor_time (adjusted_current_frame (event));
5031 }
5032
5033 void
5034 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5035 {
5036         framepos_t start = 0;
5037         framepos_t end = 0;
5038         ArdourCanvas::Rectangle *crect;
5039
5040         switch (_operation) {
5041         case CreateSkipMarker:
5042                 crect = _editor->range_bar_drag_rect;
5043                 break;
5044         case CreateRangeMarker:
5045                 crect = _editor->range_bar_drag_rect;
5046                 break;
5047         case CreateTransportMarker:
5048                 crect = _editor->transport_bar_drag_rect;
5049                 break;
5050         case CreateCDMarker:
5051                 crect = _editor->cd_marker_bar_drag_rect;
5052                 break;
5053         default:
5054                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5055                 return;
5056                 break;
5057         }
5058
5059         framepos_t const pf = adjusted_current_frame (event);
5060
5061         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5062                 framepos_t grab = grab_frame ();
5063                 _editor->snap_to (grab);
5064
5065                 if (pf < grab_frame()) {
5066                         start = pf;
5067                         end = grab;
5068                 } else {
5069                         end = pf;
5070                         start = grab;
5071                 }
5072
5073                 /* first drag: Either add to the selection
5074                    or create a new selection.
5075                 */
5076
5077                 if (first_move) {
5078
5079                         _editor->temp_location->set (start, end);
5080
5081                         crect->show ();
5082
5083                         update_item (_editor->temp_location);
5084                         _drag_rect->show();
5085                         //_drag_rect->raise_to_top();
5086
5087                 }
5088         }
5089
5090         if (start != end) {
5091                 _editor->temp_location->set (start, end);
5092
5093                 double x1 = _editor->sample_to_pixel (start);
5094                 double x2 = _editor->sample_to_pixel (end);
5095                 crect->set_x0 (x1);
5096                 crect->set_x1 (x2);
5097
5098                 update_item (_editor->temp_location);
5099         }
5100
5101         show_verbose_cursor_time (pf);
5102
5103 }
5104
5105 void
5106 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5107 {
5108         Location * newloc = 0;
5109         string rangename;
5110         int flags;
5111
5112         if (movement_occurred) {
5113                 motion (event, false);
5114                 _drag_rect->hide();
5115
5116                 switch (_operation) {
5117                 case CreateSkipMarker:
5118                 case CreateRangeMarker:
5119                 case CreateCDMarker:
5120                     {
5121                         XMLNode &before = _editor->session()->locations()->get_state();
5122                         if (_operation == CreateSkipMarker) {
5123                                 _editor->begin_reversible_command (_("new skip marker"));
5124                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5125                                 flags = Location::IsRangeMarker | Location::IsSkip;
5126                                 _editor->range_bar_drag_rect->hide();
5127                         } else if (_operation == CreateCDMarker) {
5128                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5129                                 _editor->begin_reversible_command (_("new CD marker"));
5130                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5131                                 _editor->cd_marker_bar_drag_rect->hide();
5132                         } else {
5133                                 _editor->begin_reversible_command (_("new skip marker"));
5134                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5135                                 flags = Location::IsRangeMarker;
5136                                 _editor->range_bar_drag_rect->hide();
5137                         }
5138                         newloc = new Location (
5139                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5140                                 );
5141
5142                         _editor->session()->locations()->add (newloc, true);
5143                         XMLNode &after = _editor->session()->locations()->get_state();
5144                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5145                         _editor->commit_reversible_command ();
5146                         break;
5147                     }
5148
5149                 case CreateTransportMarker:
5150                         // popup menu to pick loop or punch
5151                         _editor->new_transport_marker_context_menu (&event->button, _item);
5152                         break;
5153                 }
5154
5155         } else {
5156
5157                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5158
5159                 if (_operation == CreateTransportMarker) {
5160
5161                         /* didn't drag, so just locate */
5162
5163                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5164
5165                 } else if (_operation == CreateCDMarker) {
5166
5167                         /* didn't drag, but mark is already created so do
5168                          * nothing */
5169
5170                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5171
5172                         framepos_t start;
5173                         framepos_t end;
5174
5175                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5176
5177                         if (end == max_framepos) {
5178                                 end = _editor->session()->current_end_frame ();
5179                         }
5180
5181                         if (start == max_framepos) {
5182                                 start = _editor->session()->current_start_frame ();
5183                         }
5184
5185                         switch (_editor->mouse_mode) {
5186                         case MouseObject:
5187                                 /* find the two markers on either side and then make the selection from it */
5188                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5189                                 break;
5190
5191                         case MouseRange:
5192                                 /* find the two markers on either side of the click and make the range out of it */
5193                                 _editor->selection->set (start, end);
5194                                 break;
5195
5196                         default:
5197                                 break;
5198                         }
5199                 }
5200         }
5201
5202         _editor->stop_canvas_autoscroll ();
5203 }
5204
5205 void
5206 RangeMarkerBarDrag::aborted (bool movement_occured)
5207 {
5208         if (movement_occured) {
5209                 _drag_rect->hide ();
5210         }
5211 }
5212
5213 void
5214 RangeMarkerBarDrag::update_item (Location* location)
5215 {
5216         double const x1 = _editor->sample_to_pixel (location->start());
5217         double const x2 = _editor->sample_to_pixel (location->end());
5218
5219         _drag_rect->set_x0 (x1);
5220         _drag_rect->set_x1 (x2);
5221 }
5222
5223 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5224         : Drag (e, i)
5225         , _cumulative_dx (0)
5226         , _cumulative_dy (0)
5227 {
5228         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5229
5230         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5231         assert (_primary);
5232         _region = &_primary->region_view ();
5233         _note_height = _region->midi_stream_view()->note_height ();
5234 }
5235
5236 void
5237 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5238 {
5239         Drag::start_grab (event);
5240         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5241
5242         if (!(_was_selected = _primary->selected())) {
5243
5244                 /* tertiary-click means extend selection - we'll do that on button release,
5245                    so don't add it here, because otherwise we make it hard to figure
5246                    out the "extend-to" range.
5247                 */
5248
5249                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5250
5251                 if (!extend) {
5252                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5253
5254                         if (add) {
5255                                 _region->note_selected (_primary, true);
5256                         } else {
5257                                 _region->unique_select (_primary);
5258                         }
5259
5260                         _editor->begin_reversible_selection_op(X_("Select Note Press"));
5261                         _editor->commit_reversible_selection_op();
5262                 }
5263         }
5264 }
5265
5266 /** @return Current total drag x change in frames */
5267 frameoffset_t
5268 NoteDrag::total_dx (const guint state) const
5269 {
5270         /* dx in frames */
5271         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5272
5273         /* primary note time */
5274         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5275
5276         /* new time of the primary note in session frames */
5277         frameoffset_t st = n + dx + snap_delta (state);
5278
5279         framepos_t const rp = _region->region()->position ();
5280
5281         /* prevent the note being dragged earlier than the region's position */
5282         st = max (st, rp);
5283
5284         /* possibly snap and return corresponding delta */
5285
5286         bool snap = true;
5287
5288         if (ArdourKeyboard::indicates_snap (state)) {
5289                 if (_editor->snap_mode () != SnapOff) {
5290                         snap = false;
5291                 }
5292         } else {
5293                 if (_editor->snap_mode () == SnapOff) {
5294                         snap = false;
5295                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5296                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5297                                 snap = true;
5298                         }
5299                 }
5300         }
5301
5302         frameoffset_t ret;
5303         if (snap) {
5304                 ret =  _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5305         } else {
5306                 ret = st - n - snap_delta (state);
5307         }
5308         return ret;
5309 }
5310
5311 /** @return Current total drag y change in note number */
5312 int8_t
5313 NoteDrag::total_dy () const
5314 {
5315         MidiStreamView* msv = _region->midi_stream_view ();
5316         double const y = _region->midi_view()->y_position ();
5317         /* new current note */
5318         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5319         /* clamp */
5320         n = max (msv->lowest_note(), n);
5321         n = min (msv->highest_note(), n);
5322         /* and work out delta */
5323         return n - msv->y_to_note (grab_y() - y);
5324 }
5325
5326 void
5327 NoteDrag::motion (GdkEvent * event, bool)
5328 {
5329         /* Total change in x and y since the start of the drag */
5330         frameoffset_t const dx = total_dx (event->button.state);
5331         int8_t const dy = total_dy ();
5332
5333         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5334         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5335         double const tdy = -dy * _note_height - _cumulative_dy;
5336
5337         if (tdx || tdy) {
5338                 _cumulative_dx += tdx;
5339                 _cumulative_dy += tdy;
5340
5341                 int8_t note_delta = total_dy();
5342
5343                 _region->move_selection (tdx, tdy, note_delta);
5344
5345                 /* the new note value may be the same as the old one, but we
5346                  * don't know what that means because the selection may have
5347                  * involved more than one note and we might be doing something
5348                  * odd with them. so show the note value anyway, always.
5349                  */
5350
5351                 char buf[12];
5352                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5353
5354                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5355                           (int) floor ((double)new_note));
5356
5357                 show_verbose_cursor_text (buf);
5358         }
5359 }
5360
5361 void
5362 NoteDrag::finished (GdkEvent* ev, bool moved)
5363 {
5364         if (!moved) {
5365                 /* no motion - select note */
5366
5367                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5368                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5369
5370                         bool changed = false;
5371
5372                         if (_was_selected) {
5373                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5374                                 if (add) {
5375                                         _region->note_deselected (_primary);
5376                                         changed = true;
5377                                 }
5378                         } else {
5379                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5380                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5381
5382                                 if (!extend && !add && _region->selection_size() > 1) {
5383                                         _region->unique_select (_primary);
5384                                         changed = true;
5385                                 } else if (extend) {
5386                                         _region->note_selected (_primary, true, true);
5387                                         changed = true;
5388                                 } else {
5389                                         /* it was added during button press */
5390                                 }
5391                         }
5392
5393                         if (changed) {
5394                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5395                                 _editor->commit_reversible_selection_op();
5396                         }
5397                 }
5398         } else {
5399                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5400         }
5401 }
5402
5403 void
5404 NoteDrag::aborted (bool)
5405 {
5406         /* XXX: TODO */
5407 }
5408
5409 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5410 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5411         : Drag (editor, atv->base_item ())
5412         , _ranges (r)
5413         , _y_origin (atv->y_position())
5414         , _nothing_to_drag (false)
5415 {
5416         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5417         setup (atv->lines ());
5418 }
5419
5420 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5421 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5422         : Drag (editor, rv->get_canvas_group ())
5423         , _ranges (r)
5424         , _y_origin (rv->get_time_axis_view().y_position())
5425         , _nothing_to_drag (false)
5426         , _integral (false)
5427 {
5428         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5429
5430         list<boost::shared_ptr<AutomationLine> > lines;
5431
5432         AudioRegionView*      audio_view;
5433         AutomationRegionView* automation_view;
5434         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5435                 lines.push_back (audio_view->get_gain_line ());
5436         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5437                 lines.push_back (automation_view->line ());
5438                 _integral = true;
5439         } else {
5440                 error << _("Automation range drag created for invalid region type") << endmsg;
5441         }
5442
5443         setup (lines);
5444 }
5445
5446 /** @param lines AutomationLines to drag.
5447  *  @param offset Offset from the session start to the points in the AutomationLines.
5448  */
5449 void
5450 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5451 {
5452         /* find the lines that overlap the ranges being dragged */
5453         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5454         while (i != lines.end ()) {
5455                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5456                 ++j;
5457
5458                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5459
5460                 /* check this range against all the AudioRanges that we are using */
5461                 list<AudioRange>::const_iterator k = _ranges.begin ();
5462                 while (k != _ranges.end()) {
5463                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5464                                 break;
5465                         }
5466                         ++k;
5467                 }
5468
5469                 /* add it to our list if it overlaps at all */
5470                 if (k != _ranges.end()) {
5471                         Line n;
5472                         n.line = *i;
5473                         n.state = 0;
5474                         n.range = r;
5475                         _lines.push_back (n);
5476                 }
5477
5478                 i = j;
5479         }
5480
5481         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5482 }
5483
5484 double
5485 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5486 {
5487         return 1.0 - ((global_y - _y_origin) / line->height());
5488 }
5489
5490 double
5491 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5492 {
5493         const double v = list->eval(x);
5494         return _integral ? rint(v) : v;
5495 }
5496
5497 void
5498 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5499 {
5500         Drag::start_grab (event, cursor);
5501
5502         /* Get line states before we start changing things */
5503         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5504                 i->state = &i->line->get_state ();
5505                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5506         }
5507
5508         if (_ranges.empty()) {
5509
5510                 /* No selected time ranges: drag all points */
5511                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5512                         uint32_t const N = i->line->npoints ();
5513                         for (uint32_t j = 0; j < N; ++j) {
5514                                 i->points.push_back (i->line->nth (j));
5515                         }
5516                 }
5517
5518         } else {
5519
5520                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5521
5522                         framecnt_t const half = (i->start + i->end) / 2;
5523
5524                         /* find the line that this audio range starts in */
5525                         list<Line>::iterator j = _lines.begin();
5526                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5527                                 ++j;
5528                         }
5529
5530                         if (j != _lines.end()) {
5531                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5532
5533                                 /* j is the line that this audio range starts in; fade into it;
5534                                    64 samples length plucked out of thin air.
5535                                 */
5536
5537                                 framepos_t a = i->start + 64;
5538                                 if (a > half) {
5539                                         a = half;
5540                                 }
5541
5542                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5543                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5544
5545                                 the_list->editor_add (p, value (the_list, p), false);
5546                                 the_list->editor_add (q, value (the_list, q), false);
5547                         }
5548
5549                         /* same thing for the end */
5550
5551                         j = _lines.begin();
5552                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5553                                 ++j;
5554                         }
5555
5556                         if (j != _lines.end()) {
5557                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5558
5559                                 /* j is the line that this audio range starts in; fade out of it;
5560                                    64 samples length plucked out of thin air.
5561                                 */
5562
5563                                 framepos_t b = i->end - 64;
5564                                 if (b < half) {
5565                                         b = half;
5566                                 }
5567
5568                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5569                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5570
5571                                 the_list->editor_add (p, value (the_list, p), false);
5572                                 the_list->editor_add (q, value (the_list, q), false);
5573                         }
5574                 }
5575
5576                 _nothing_to_drag = true;
5577
5578                 /* Find all the points that should be dragged and put them in the relevant
5579                    points lists in the Line structs.
5580                 */
5581
5582                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5583
5584                         uint32_t const N = i->line->npoints ();
5585                         for (uint32_t j = 0; j < N; ++j) {
5586
5587                                 /* here's a control point on this line */
5588                                 ControlPoint* p = i->line->nth (j);
5589                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5590
5591                                 /* see if it's inside a range */
5592                                 list<AudioRange>::const_iterator k = _ranges.begin ();
5593                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5594                                         ++k;
5595                                 }
5596
5597                                 if (k != _ranges.end()) {
5598                                         /* dragging this point */
5599                                         _nothing_to_drag = false;
5600                                         i->points.push_back (p);
5601                                 }
5602                         }
5603                 }
5604         }
5605
5606         if (_nothing_to_drag) {
5607                 return;
5608         }
5609         _editor->begin_reversible_command (_("automation range move"));
5610         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5611                 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5612         }
5613 }
5614
5615 void
5616 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5617 {
5618         if (_nothing_to_drag) {
5619                 return;
5620         }
5621
5622         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5623                 float const f = y_fraction (l->line, current_pointer_y());
5624                 /* we are ignoring x position for this drag, so we can just pass in anything */
5625                 uint32_t ignored;
5626                 l->line->drag_motion (0, f, true, false, ignored);
5627                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5628         }
5629 }
5630
5631 void
5632 AutomationRangeDrag::finished (GdkEvent* event, bool)
5633 {
5634         if (_nothing_to_drag) {
5635                 return;
5636         }
5637
5638         motion (event, false);
5639         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5640                 i->line->end_drag (false, 0);
5641         }
5642
5643         _editor->commit_reversible_command ();
5644 }
5645
5646 void
5647 AutomationRangeDrag::aborted (bool)
5648 {
5649         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5650                 i->line->reset ();
5651         }
5652 }
5653
5654 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5655         : view (v)
5656         , initial_time_axis_view (itav)
5657 {
5658         /* note that time_axis_view may be null if the regionview was created
5659          * as part of a copy operation.
5660          */
5661         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5662         layer = v->region()->layer ();
5663         initial_y = v->get_canvas_group()->position().y;
5664         initial_playlist = v->region()->playlist ();
5665         initial_position = v->region()->position ();
5666         initial_end = v->region()->position () + v->region()->length ();
5667 }
5668
5669 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5670         : Drag (e, i->canvas_item ())
5671         , _region_view (r)
5672         , _patch_change (i)
5673         , _cumulative_dx (0)
5674 {
5675         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5676                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5677                                                    grab_frame()));
5678 }
5679
5680 void
5681 PatchChangeDrag::motion (GdkEvent* ev, bool)
5682 {
5683         framepos_t f = adjusted_current_frame (ev);
5684         boost::shared_ptr<Region> r = _region_view->region ();
5685         f = max (f, r->position ());
5686         f = min (f, r->last_frame ());
5687
5688         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5689         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5690         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5691         _cumulative_dx = dxu;
5692 }
5693
5694 void
5695 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5696 {
5697         if (!movement_occurred) {
5698                 return;
5699         }
5700
5701         boost::shared_ptr<Region> r (_region_view->region ());
5702         framepos_t f = adjusted_current_frame (ev);
5703         f = max (f, r->position ());
5704         f = min (f, r->last_frame ());
5705
5706         _region_view->move_patch_change (
5707                 *_patch_change,
5708                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5709                 );
5710 }
5711
5712 void
5713 PatchChangeDrag::aborted (bool)
5714 {
5715         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5716 }
5717
5718 void
5719 PatchChangeDrag::setup_pointer_frame_offset ()
5720 {
5721         boost::shared_ptr<Region> region = _region_view->region ();
5722         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5723 }
5724
5725 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5726         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5727         , _region_view (rv)
5728 {
5729
5730 }
5731
5732 void
5733 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5734 {
5735         _region_view->update_drag_selection (
5736                 x1, x2, y1, y2,
5737                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5738 }
5739
5740 void
5741 MidiRubberbandSelectDrag::deselect_things ()
5742 {
5743         /* XXX */
5744 }
5745
5746 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5747         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5748         , _region_view (rv)
5749 {
5750         _vertical_only = true;
5751 }
5752
5753 void
5754 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5755 {
5756         double const y = _region_view->midi_view()->y_position ();
5757
5758         y1 = max (0.0, y1 - y);
5759         y2 = max (0.0, y2 - y);
5760
5761         _region_view->update_vertical_drag_selection (
5762                 y1,
5763                 y2,
5764                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5765                 );
5766 }
5767
5768 void
5769 MidiVerticalSelectDrag::deselect_things ()
5770 {
5771         /* XXX */
5772 }
5773
5774 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5775         : RubberbandSelectDrag (e, i)
5776 {
5777
5778 }
5779
5780 void
5781 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5782 {
5783         if (drag_in_progress) {
5784                 /* We just want to select things at the end of the drag, not during it */
5785                 return;
5786         }
5787
5788         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5789
5790         _editor->begin_reversible_selection_op (X_("rubberband selection"));
5791
5792         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5793
5794         _editor->commit_reversible_selection_op ();
5795 }
5796
5797 void
5798 EditorRubberbandSelectDrag::deselect_things ()
5799 {
5800         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5801
5802         _editor->selection->clear_tracks();
5803         _editor->selection->clear_regions();
5804         _editor->selection->clear_points ();
5805         _editor->selection->clear_lines ();
5806         _editor->selection->clear_midi_notes ();
5807
5808         _editor->commit_reversible_selection_op();
5809 }
5810
5811 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5812         : Drag (e, i)
5813         , _region_view (rv)
5814         , _drag_rect (0)
5815 {
5816         _note[0] = _note[1] = 0;
5817 }
5818
5819 NoteCreateDrag::~NoteCreateDrag ()
5820 {
5821         delete _drag_rect;
5822 }
5823
5824 framecnt_t
5825 NoteCreateDrag::grid_frames (framepos_t t) const
5826 {
5827         bool success;
5828         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5829         if (!success) {
5830                 grid_beats = Evoral::Beats(1);
5831         }
5832
5833         return _region_view->region_beats_to_region_frames (grid_beats);
5834 }
5835
5836 void
5837 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5838 {
5839         Drag::start_grab (event, cursor);
5840
5841         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5842
5843         framepos_t pf = _drags->current_pointer_frame ();
5844         framecnt_t const g = grid_frames (pf);
5845
5846         /* Hack so that we always snap to the note that we are over, instead of snapping
5847            to the next one if we're more than halfway through the one we're over.
5848         */
5849         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5850                 pf -= g / 2;
5851         }
5852
5853         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5854         _note[1] = _note[0];
5855
5856         MidiStreamView* sv = _region_view->midi_stream_view ();
5857         double const x = _editor->sample_to_pixel (_note[0]);
5858         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5859
5860         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5861         _drag_rect->set_outline_all ();
5862         _drag_rect->set_outline_color (0xffffff99);
5863         _drag_rect->set_fill_color (0xffffff66);
5864 }
5865
5866 void
5867 NoteCreateDrag::motion (GdkEvent* event, bool)
5868 {
5869         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5870         double const x0 = _editor->sample_to_pixel (_note[0]);
5871         double const x1 = _editor->sample_to_pixel (_note[1]);
5872         _drag_rect->set_x0 (std::min(x0, x1));
5873         _drag_rect->set_x1 (std::max(x0, x1));
5874 }
5875
5876 void
5877 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5878 {
5879         if (!had_movement) {
5880                 return;
5881         }
5882
5883         framepos_t const start = min (_note[0], _note[1]);
5884         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5885
5886         framecnt_t const g = grid_frames (start);
5887         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5888
5889         if (_editor->snap_mode() == SnapNormal && length < g) {
5890                 length = g;
5891         }
5892
5893         Evoral::Beats length_beats = max (
5894                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5895
5896         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5897 }
5898
5899 double
5900 NoteCreateDrag::y_to_region (double y) const
5901 {
5902         double x = 0;
5903         _region_view->get_canvas_group()->canvas_to_item (x, y);
5904         return y;
5905 }
5906
5907 void
5908 NoteCreateDrag::aborted (bool)
5909 {
5910
5911 }
5912
5913 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5914         : Drag (e, i)
5915         , arv (rv)
5916         , start (start_yn)
5917 {
5918         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5919 }
5920
5921 void
5922 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5923 {
5924         Drag::start_grab (event, cursor);
5925 }
5926
5927 void
5928 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5929 {
5930         double distance;
5931         double new_length;
5932         framecnt_t len;
5933
5934         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5935
5936         if (start) {
5937                 distance = _drags->current_pointer_x() - grab_x();
5938                 len = ar->fade_in()->back()->when;
5939         } else {
5940                 distance = grab_x() - _drags->current_pointer_x();
5941                 len = ar->fade_out()->back()->when;
5942         }
5943
5944         /* how long should it be ? */
5945
5946         new_length = len + _editor->pixel_to_sample (distance);
5947
5948         /* now check with the region that this is legal */
5949
5950         new_length = ar->verify_xfade_bounds (new_length, start);
5951
5952         if (start) {
5953                 arv->reset_fade_in_shape_width (ar, new_length);
5954         } else {
5955                 arv->reset_fade_out_shape_width (ar, new_length);
5956         }
5957 }
5958
5959 void
5960 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5961 {
5962         double distance;
5963         double new_length;
5964         framecnt_t len;
5965
5966         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5967
5968         if (start) {
5969                 distance = _drags->current_pointer_x() - grab_x();
5970                 len = ar->fade_in()->back()->when;
5971         } else {
5972                 distance = grab_x() - _drags->current_pointer_x();
5973                 len = ar->fade_out()->back()->when;
5974         }
5975
5976         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5977
5978         _editor->begin_reversible_command ("xfade trim");
5979         ar->playlist()->clear_owned_changes ();
5980
5981         if (start) {
5982                 ar->set_fade_in_length (new_length);
5983         } else {
5984                 ar->set_fade_out_length (new_length);
5985         }
5986
5987         /* Adjusting the xfade may affect other regions in the playlist, so we need
5988            to get undo Commands from the whole playlist rather than just the
5989            region.
5990         */
5991
5992         vector<Command*> cmds;
5993         ar->playlist()->rdiff (cmds);
5994         _editor->session()->add_commands (cmds);
5995         _editor->commit_reversible_command ();
5996
5997 }
5998
5999 void
6000 CrossfadeEdgeDrag::aborted (bool)
6001 {
6002         if (start) {
6003                 // arv->redraw_start_xfade ();
6004         } else {
6005                 // arv->redraw_end_xfade ();
6006         }
6007 }
6008
6009 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6010         : Drag (e, item, true)
6011         , line (new EditorCursor (*e))
6012 {
6013         line->set_position (pos);
6014         line->show ();
6015 }
6016
6017 RegionCutDrag::~RegionCutDrag ()
6018 {
6019         delete line;
6020 }
6021
6022 void
6023 RegionCutDrag::motion (GdkEvent*, bool)
6024 {
6025         framepos_t where = _drags->current_pointer_frame();
6026         _editor->snap_to (where);
6027
6028         line->set_position (where);
6029 }
6030
6031 void
6032 RegionCutDrag::finished (GdkEvent*, bool)
6033 {
6034         _editor->get_track_canvas()->canvas()->re_enter();
6035
6036         framepos_t pos = _drags->current_pointer_frame();
6037
6038         line->hide ();
6039
6040         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6041
6042         if (rs.empty()) {
6043                 return;
6044         }
6045
6046         _editor->split_regions_at (pos, rs);
6047 }
6048
6049 void
6050 RegionCutDrag::aborted (bool)
6051 {
6052 }