1a5803d49bb3c96a5648df15303e9f0b165b6e7e
[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         , relative (false)
2338         , at_front (true)
2339         , _snap_delta (0)
2340 {
2341         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2342 }
2343
2344 void
2345 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2346 {
2347         Gdk::Cursor* cursor;
2348         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2349         assert (cnote);
2350         float x_fraction = cnote->mouse_x_fraction ();
2351
2352         if (x_fraction > 0.0 && x_fraction < 0.25) {
2353                 cursor = _editor->cursors()->left_side_trim;
2354                 at_front = true;
2355         } else  {
2356                 cursor = _editor->cursors()->right_side_trim;
2357                 at_front = false;
2358         }
2359
2360         Drag::start_grab (event, cursor);
2361
2362         region = &cnote->region_view();
2363
2364         double temp;
2365         temp = region->snap_to_pixel (cnote->x0 (), true);
2366         _snap_delta = temp - cnote->x0 ();
2367
2368         _item->grab ();
2369
2370         if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2371                 relative = false;
2372         } else {
2373                 relative = true;
2374         }
2375         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2376         if (ms.size() > 1) {
2377                 /* has to be relative, may make no sense otherwise */
2378                 relative = true;
2379         }
2380         /* select this note; if it is already selected, preserve the existing selection,
2381            otherwise make this note the only one selected.
2382         */
2383         region->note_selected (cnote, cnote->selected ());
2384 }
2385
2386 void
2387 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2388 {
2389         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2390         if (first_move) {
2391                 _editor->begin_reversible_command (_("resize notes"));
2392
2393                 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2394                         MidiRegionSelection::iterator next;
2395                         next = r;
2396                         ++next;
2397                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2398                         if (mrv) {
2399                                 mrv->begin_resizing (at_front);
2400                         }
2401                         r = next;
2402                 }
2403         }
2404
2405         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2406                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2407                 assert (nb);
2408                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2409                 if (mrv) {
2410                         double sd = 0.0;
2411                         bool snap = true;
2412                         bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2413
2414                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2415                                 if (_editor->snap_mode () != SnapOff) {
2416                                         snap = false;
2417                                 }
2418                         } else {
2419                                 if (_editor->snap_mode () == SnapOff) {
2420                                         snap = false;
2421                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2422                                         if (apply_snap_delta) {
2423                                                 snap = true;
2424                                         }
2425                                 }
2426                         }
2427
2428                         if (apply_snap_delta) {
2429                                 sd = _snap_delta;
2430                         }
2431
2432                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2433                 }
2434         }
2435 }
2436
2437 void
2438 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2439 {
2440         if (!movement_occurred) {
2441                 return;
2442         }
2443
2444         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2445         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2446                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2447                 assert (nb);
2448                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2449                 double sd = 0.0;
2450                 bool snap = true;
2451                 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2452                 if (mrv) {
2453                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2454                                 if (_editor->snap_mode () != SnapOff) {
2455                                         snap = false;
2456                                 }
2457                         } else {
2458                                 if (_editor->snap_mode () == SnapOff) {
2459                                         snap = false;
2460                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2461                                         if (apply_snap_delta) {
2462                                                 snap = true;
2463                                         }
2464                                 }
2465                         }
2466
2467                         if (apply_snap_delta) {
2468                                 sd = _snap_delta;
2469                         }
2470
2471                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2472                 }
2473         }
2474
2475         _editor->commit_reversible_command ();
2476 }
2477
2478 void
2479 NoteResizeDrag::aborted (bool)
2480 {
2481         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2482         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2483                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2484                 if (mrv) {
2485                         mrv->abort_resizing ();
2486                 }
2487         }
2488 }
2489
2490 AVDraggingView::AVDraggingView (RegionView* v)
2491         : view (v)
2492 {
2493         initial_position = v->region()->position ();
2494 }
2495
2496 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2497         : Drag (e, i)
2498 {
2499         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2500
2501         RegionSelection rs;
2502         TrackViewList empty;
2503         empty.clear();
2504         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2505         std::list<RegionView*> views = rs.by_layer();
2506
2507         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2508                 RegionView* rv = (*i);
2509                 if (!rv->region()->video_locked()) {
2510                         continue;
2511                 }
2512                 _views.push_back (AVDraggingView (rv));
2513         }
2514 }
2515
2516 void
2517 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2518 {
2519         Drag::start_grab (event);
2520         if (_editor->session() == 0) {
2521                 return;
2522         }
2523
2524         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2525         _max_backwards_drag = (
2526                           ARDOUR_UI::instance()->video_timeline->get_duration()
2527                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2528                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2529                         );
2530
2531         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2532                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2533                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2534                 }
2535         }
2536         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2537
2538         char buf[128];
2539         Timecode::Time timecode;
2540         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2541         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);
2542         show_verbose_cursor_text (buf);
2543 }
2544
2545 void
2546 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2547 {
2548         if (_editor->session() == 0) {
2549                 return;
2550         }
2551         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2552                 return;
2553         }
2554
2555         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2556         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2557
2558         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2559                 dt = - _max_backwards_drag;
2560         }
2561
2562         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2563         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2564
2565         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2566                 RegionView* rv = i->view;
2567                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2568                 if (first_move) {
2569                         rv->drag_start ();
2570                         rv->region()->clear_changes ();
2571                         rv->region()->suspend_property_changes();
2572                 }
2573                 rv->region()->set_position(i->initial_position + dt);
2574                 rv->region_changed(ARDOUR::Properties::position);
2575         }
2576
2577         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2578         Timecode::Time timecode;
2579         Timecode::Time timediff;
2580         char buf[128];
2581         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2582         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2583         snprintf (buf, sizeof (buf),
2584                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2585                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2586                         , _("Video Start:"),
2587                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2588                         , _("Diff:"),
2589                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2590                                 );
2591         show_verbose_cursor_text (buf);
2592 }
2593
2594 void
2595 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2596 {
2597         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2598                 return;
2599         }
2600
2601         if (!movement_occurred || ! _editor->session()) {
2602                 return;
2603         }
2604
2605         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2606
2607         _editor->begin_reversible_command (_("Move Video"));
2608
2609         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2610         ARDOUR_UI::instance()->video_timeline->save_undo();
2611         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2612         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2613
2614         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2615                 i->view->drag_end();
2616                 i->view->region()->resume_property_changes ();
2617
2618                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2619         }
2620
2621         _editor->session()->maybe_update_session_range(
2622                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2623                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2624                         );
2625
2626
2627         _editor->commit_reversible_command ();
2628 }
2629
2630 void
2631 VideoTimeLineDrag::aborted (bool)
2632 {
2633         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2634                 return;
2635         }
2636         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2637         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2638
2639         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2640                 i->view->region()->resume_property_changes ();
2641                 i->view->region()->set_position(i->initial_position);
2642         }
2643 }
2644
2645 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2646         : RegionDrag (e, i, p, v)
2647         , _preserve_fade_anchor (preserve_fade_anchor)
2648         , _jump_position_when_done (false)
2649 {
2650         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2651 }
2652
2653 void
2654 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2655 {
2656         double speed = 1.0;
2657         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2658         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2659
2660         if (tv && tv->is_track()) {
2661                 speed = tv->track()->speed();
2662         }
2663
2664         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2665         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2666         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2667
2668         framepos_t const pf = adjusted_current_frame (event);
2669         setup_snap_delta (region_start);
2670
2671         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2672                 /* Move the contents of the region around without changing the region bounds */
2673                 _operation = ContentsTrim;
2674                 Drag::start_grab (event, _editor->cursors()->trimmer);
2675         } else {
2676                 /* These will get overridden for a point trim.*/
2677                 if (pf < (region_start + region_length/2)) {
2678                         /* closer to front */
2679                         _operation = StartTrim;
2680                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2681                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2682                         } else {
2683                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2684                         }
2685                 } else {
2686                         /* closer to end */
2687                         _operation = EndTrim;
2688                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2689                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2690                         } else {
2691                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2692                         }
2693                 }
2694         }
2695         /* jump trim disabled for now
2696         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2697                 _jump_position_when_done = true;
2698         }
2699         */
2700
2701         switch (_operation) {
2702         case StartTrim:
2703                 show_verbose_cursor_time (region_start);
2704                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2705                         i->view->trim_front_starting ();
2706                 }
2707                 break;
2708         case EndTrim:
2709                 show_verbose_cursor_duration (region_start, region_end);
2710                 break;
2711         case ContentsTrim:
2712                 show_verbose_cursor_time (pf);
2713                 break;
2714         }
2715
2716         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2717                 i->view->region()->suspend_property_changes ();
2718         }
2719 }
2720
2721 void
2722 TrimDrag::motion (GdkEvent* event, bool first_move)
2723 {
2724         RegionView* rv = _primary;
2725
2726         double speed = 1.0;
2727         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2728         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2729         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2730         frameoffset_t frame_delta = 0;
2731
2732         if (tv && tv->is_track()) {
2733                 speed = tv->track()->speed();
2734         }
2735         framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2736         framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2737
2738         if (first_move) {
2739
2740                 string trim_type;
2741
2742                 switch (_operation) {
2743                 case StartTrim:
2744                         trim_type = "Region start trim";
2745                         break;
2746                 case EndTrim:
2747                         trim_type = "Region end trim";
2748                         break;
2749                 case ContentsTrim:
2750                         trim_type = "Region content trim";
2751                         break;
2752                 default:
2753                         assert(0);
2754                         break;
2755                 }
2756
2757                 _editor->begin_reversible_command (trim_type);
2758
2759                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2760                         RegionView* rv = i->view;
2761                         rv->enable_display (false);
2762                         rv->region()->playlist()->clear_owned_changes ();
2763
2764                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2765
2766                         if (arv) {
2767                                 arv->temporarily_hide_envelope ();
2768                                 arv->drag_start ();
2769                         }
2770
2771                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2772                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2773
2774                         if (insert_result.second) {
2775                                 pl->freeze();
2776                         }
2777                 }
2778         }
2779
2780         bool non_overlap_trim = false;
2781
2782         if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2783                 non_overlap_trim = true;
2784         }
2785
2786         /* contstrain trim to fade length */
2787         if (_preserve_fade_anchor) {
2788                 switch (_operation) {
2789                         case StartTrim:
2790                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2791                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2792                                         if (!arv) continue;
2793                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2794                                         if (ar->locked()) continue;
2795                                         framecnt_t len = ar->fade_in()->back()->when;
2796                                         if (len < dt) dt = min(dt, len);
2797                                 }
2798                                 break;
2799                         case EndTrim:
2800                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2801                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2802                                         if (!arv) continue;
2803                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2804                                         if (ar->locked()) continue;
2805                                         framecnt_t len = ar->fade_out()->back()->when;
2806                                         if (len < -dt) dt = max(dt, -len);
2807                                 }
2808                                 break;
2809                         case ContentsTrim:
2810                                 break;
2811                 }
2812         }
2813
2814
2815         switch (_operation) {
2816         case StartTrim:
2817                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2818                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2819                         if (changed && _preserve_fade_anchor) {
2820                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2821                                 if (arv) {
2822                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2823                                         framecnt_t len = ar->fade_in()->back()->when;
2824                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2825                                         framepos_t new_length = len - diff;
2826                                         i->anchored_fade_length = min (ar->length(), new_length);
2827                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2828                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2829                                 }
2830                         }
2831                 }
2832                 break;
2833
2834         case EndTrim:
2835                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2836                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2837                         if (changed && _preserve_fade_anchor) {
2838                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2839                                 if (arv) {
2840                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2841                                         framecnt_t len = ar->fade_out()->back()->when;
2842                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2843                                         framepos_t new_length = len + diff;
2844                                         i->anchored_fade_length = min (ar->length(), new_length);
2845                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2846                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2847                                 }
2848                         }
2849                 }
2850                 break;
2851
2852         case ContentsTrim:
2853                 {
2854                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2855
2856                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2857                                 i->view->move_contents (frame_delta);
2858                         }
2859                 }
2860                 break;
2861         }
2862
2863         switch (_operation) {
2864         case StartTrim:
2865                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2866                 break;
2867         case EndTrim:
2868                 show_verbose_cursor_duration ((framepos_t)  rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2869                 break;
2870         case ContentsTrim:
2871                 // show_verbose_cursor_time (frame_delta);
2872                 break;
2873         }
2874 }
2875
2876 void
2877 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2878 {
2879         if (movement_occurred) {
2880                 motion (event, false);
2881
2882                 if (_operation == StartTrim) {
2883                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2884                                 {
2885                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2886                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2887                                            needs to be zero in the undo record.
2888                                         */
2889                                         i->view->trim_front_ending ();
2890                                 }
2891                                 if (_preserve_fade_anchor) {
2892                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2893                                         if (arv) {
2894                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2895                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2896                                                 ar->set_fade_in_length(i->anchored_fade_length);
2897                                                 ar->set_fade_in_active(true);
2898                                         }
2899                                 }
2900                                 if (_jump_position_when_done) {
2901                                         i->view->region()->set_position (i->initial_position);
2902                                 }
2903                         }
2904                 } else if (_operation == EndTrim) {
2905                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2906                                 if (_preserve_fade_anchor) {
2907                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2908                                         if (arv) {
2909                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2910                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2911                                                 ar->set_fade_out_length(i->anchored_fade_length);
2912                                                 ar->set_fade_out_active(true);
2913                                         }
2914                                 }
2915                                 if (_jump_position_when_done) {
2916                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
2917                                 }
2918                         }
2919                 }
2920
2921                 if (!_views.empty()) {
2922                         if (_operation == StartTrim) {
2923                                 _editor->maybe_locate_with_edit_preroll(
2924                                         _views.begin()->view->region()->position());
2925                         }
2926                         if (_operation == EndTrim) {
2927                                 _editor->maybe_locate_with_edit_preroll(
2928                                         _views.begin()->view->region()->position() +
2929                                         _views.begin()->view->region()->length());
2930                         }
2931                 }
2932
2933                 if (!_editor->selection->selected (_primary)) {
2934                         _primary->thaw_after_trim ();
2935                 } else {
2936
2937                         set<boost::shared_ptr<Playlist> > diffed_playlists;
2938
2939                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2940                                 i->view->thaw_after_trim ();
2941                                 i->view->enable_display (true);
2942
2943                                 /* Trimming one region may affect others on the playlist, so we need
2944                                    to get undo Commands from the whole playlist rather than just the
2945                                    region.  Use diffed_playlists to make sure we don't diff a given
2946                                    playlist more than once.
2947                                 */
2948                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2949                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2950                                         vector<Command*> cmds;
2951                                         p->rdiff (cmds);
2952                                         _editor->session()->add_commands (cmds);
2953                                         diffed_playlists.insert (p);
2954                                 }
2955                         }
2956                 }
2957
2958                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2959                         (*p)->thaw ();
2960                 }
2961
2962                 _editor->motion_frozen_playlists.clear ();
2963                 _editor->commit_reversible_command();
2964
2965         } else {
2966                 /* no mouse movement */
2967                 _editor->point_trim (event, adjusted_current_frame (event));
2968         }
2969
2970         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2971                 if (_operation == StartTrim) {
2972                         i->view->trim_front_ending ();
2973                 }
2974
2975                 i->view->region()->resume_property_changes ();
2976         }
2977 }
2978
2979 void
2980 TrimDrag::aborted (bool movement_occurred)
2981 {
2982         /* Our motion method is changing model state, so use the Undo system
2983            to cancel.  Perhaps not ideal, as this will leave an Undo point
2984            behind which may be slightly odd from the user's point of view.
2985         */
2986
2987         finished (0, true);
2988
2989         if (movement_occurred) {
2990                 _editor->undo ();
2991         }
2992
2993         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2994                 i->view->region()->resume_property_changes ();
2995         }
2996 }
2997
2998 void
2999 TrimDrag::setup_pointer_frame_offset ()
3000 {
3001         list<DraggingView>::iterator i = _views.begin ();
3002         while (i != _views.end() && i->view != _primary) {
3003                 ++i;
3004         }
3005
3006         if (i == _views.end()) {
3007                 return;
3008         }
3009
3010         switch (_operation) {
3011         case StartTrim:
3012                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3013                 break;
3014         case EndTrim:
3015                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3016                 break;
3017         case ContentsTrim:
3018                 break;
3019         }
3020 }
3021
3022 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3023         : Drag (e, i),
3024           _copy (c)
3025 {
3026         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3027         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3028         assert (_marker);
3029 }
3030
3031 void
3032 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3033 {
3034         Drag::start_grab (event, cursor);
3035         show_verbose_cursor_time (adjusted_current_frame(event));
3036 }
3037
3038 void
3039 MeterMarkerDrag::setup_pointer_frame_offset ()
3040 {
3041         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3042 }
3043
3044 void
3045 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3046 {
3047         if (!_marker->meter().movable()) {
3048                 return;
3049         }
3050
3051         if (first_move) {
3052
3053                 // create a dummy marker for visual representation of moving the
3054                 // section, because whether its a copy or not, we're going to
3055                 // leave or lose the original marker (leave if its a copy; lose if its
3056                 // not, because we'll remove it from the map).
3057
3058                 MeterSection section (_marker->meter());
3059
3060                 if (!section.movable()) {
3061                         return;
3062                 }
3063
3064                 char name[64];
3065                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3066
3067                 _marker = new MeterMarker (
3068                         *_editor,
3069                         *_editor->meter_group,
3070                         ARDOUR_UI::config()->color ("meter marker"),
3071                         name,
3072                         *new MeterSection (_marker->meter())
3073                 );
3074
3075                 /* use the new marker for the grab */
3076                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3077
3078                 if (!_copy) {
3079                         TempoMap& map (_editor->session()->tempo_map());
3080                         /* get current state */
3081                         before_state = &map.get_state();
3082                         /* remove the section while we drag it */
3083                         map.remove_meter (section, true);
3084                 }
3085         }
3086
3087         framepos_t const pf = adjusted_current_frame (event);
3088
3089         _marker->set_position (pf);
3090         show_verbose_cursor_time (pf);
3091 }
3092
3093 void
3094 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3095 {
3096         if (!movement_occurred) {
3097                 if (was_double_click()) {
3098                         _editor->edit_meter_marker (*_marker);
3099                 }
3100                 return;
3101         }
3102
3103         if (!_marker->meter().movable()) {
3104                 return;
3105         }
3106
3107         motion (event, false);
3108
3109         Timecode::BBT_Time when;
3110
3111         TempoMap& map (_editor->session()->tempo_map());
3112         map.bbt_time (last_pointer_frame(), when);
3113
3114         if (_copy == true) {
3115                 _editor->begin_reversible_command (_("copy meter mark"));
3116                 XMLNode &before = map.get_state();
3117                 map.add_meter (_marker->meter(), when);
3118                 XMLNode &after = map.get_state();
3119                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3120                 _editor->commit_reversible_command ();
3121
3122         } else {
3123                 _editor->begin_reversible_command (_("move meter mark"));
3124
3125                 /* we removed it before, so add it back now */
3126
3127                 map.add_meter (_marker->meter(), when);
3128                 XMLNode &after = map.get_state();
3129                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3130                 _editor->commit_reversible_command ();
3131         }
3132
3133         // delete the dummy marker we used for visual representation while moving.
3134         // a new visual marker will show up automatically.
3135         delete _marker;
3136 }
3137
3138 void
3139 MeterMarkerDrag::aborted (bool moved)
3140 {
3141         _marker->set_position (_marker->meter().frame ());
3142
3143         if (moved) {
3144                 TempoMap& map (_editor->session()->tempo_map());
3145                 /* we removed it before, so add it back now */
3146                 map.add_meter (_marker->meter(), _marker->meter().frame());
3147                 // delete the dummy marker we used for visual representation while moving.
3148                 // a new visual marker will show up automatically.
3149                 delete _marker;
3150         }
3151 }
3152
3153 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3154         : Drag (e, i),
3155           _copy (c)
3156 {
3157         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3158
3159         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3160         assert (_marker);
3161 }
3162
3163 void
3164 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3165 {
3166         Drag::start_grab (event, cursor);
3167         show_verbose_cursor_time (adjusted_current_frame (event));
3168 }
3169
3170 void
3171 TempoMarkerDrag::setup_pointer_frame_offset ()
3172 {
3173         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3174 }
3175
3176 void
3177 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3178 {
3179         if (!_marker->tempo().movable()) {
3180                 return;
3181         }
3182
3183         if (first_move) {
3184
3185                 // create a dummy marker for visual representation of moving the
3186                 // section, because whether its a copy or not, we're going to
3187                 // leave or lose the original marker (leave if its a copy; lose if its
3188                 // not, because we'll remove it from the map).
3189
3190                 // create a dummy marker for visual representation of moving the copy.
3191                 // The actual copying is not done before we reach the finish callback.
3192
3193                 char name[64];
3194                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3195
3196                 TempoSection section (_marker->tempo());
3197
3198                 _marker = new TempoMarker (
3199                         *_editor,
3200                         *_editor->tempo_group,
3201                         ARDOUR_UI::config()->color ("tempo marker"),
3202                         name,
3203                         *new TempoSection (_marker->tempo())
3204                         );
3205
3206                 /* use the new marker for the grab */
3207                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3208
3209                 if (!_copy) {
3210                         TempoMap& map (_editor->session()->tempo_map());
3211                         /* get current state */
3212                         before_state = &map.get_state();
3213                         /* remove the section while we drag it */
3214                         map.remove_tempo (section, true);
3215                 }
3216         }
3217
3218         framepos_t const pf = adjusted_current_frame (event);
3219         _marker->set_position (pf);
3220         show_verbose_cursor_time (pf);
3221 }
3222
3223 void
3224 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3225 {
3226         if (!movement_occurred) {
3227                 if (was_double_click()) {
3228                         _editor->edit_tempo_marker (*_marker);
3229                 }
3230                 return;
3231         }
3232
3233         if (!_marker->tempo().movable()) {
3234                 return;
3235         }
3236
3237         motion (event, false);
3238
3239         TempoMap& map (_editor->session()->tempo_map());
3240         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3241         Timecode::BBT_Time when;
3242
3243         map.bbt_time (beat_time, when);
3244
3245         if (_copy == true) {
3246                 _editor->begin_reversible_command (_("copy tempo mark"));
3247                 XMLNode &before = map.get_state();
3248                 map.add_tempo (_marker->tempo(), when);
3249                 XMLNode &after = map.get_state();
3250                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3251                 _editor->commit_reversible_command ();
3252
3253         } else {
3254                 _editor->begin_reversible_command (_("move tempo mark"));
3255                 /* we removed it before, so add it back now */
3256                 map.add_tempo (_marker->tempo(), when);
3257                 XMLNode &after = map.get_state();
3258                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3259                 _editor->commit_reversible_command ();
3260         }
3261
3262         // delete the dummy marker we used for visual representation while moving.
3263         // a new visual marker will show up automatically.
3264         delete _marker;
3265 }
3266
3267 void
3268 TempoMarkerDrag::aborted (bool moved)
3269 {
3270         _marker->set_position (_marker->tempo().frame());
3271         if (moved) {
3272                 TempoMap& map (_editor->session()->tempo_map());
3273                 /* we removed it before, so add it back now */
3274                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3275                 // delete the dummy marker we used for visual representation while moving.
3276                 // a new visual marker will show up automatically.
3277                 delete _marker;
3278         }
3279 }
3280
3281 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3282         : Drag (e, &c.track_canvas_item(), false)
3283         , _cursor (c)
3284         , _stop (s)
3285 {
3286         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3287 }
3288
3289 /** Do all the things we do when dragging the playhead to make it look as though
3290  *  we have located, without actually doing the locate (because that would cause
3291  *  the diskstream buffers to be refilled, which is too slow).
3292  */
3293 void
3294 CursorDrag::fake_locate (framepos_t t)
3295 {
3296         _editor->playhead_cursor->set_position (t);
3297
3298         Session* s = _editor->session ();
3299         if (s->timecode_transmission_suspended ()) {
3300                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3301                 /* This is asynchronous so it will be sent "now"
3302                  */
3303                 s->send_mmc_locate (f);
3304                 /* These are synchronous and will be sent during the next
3305                    process cycle
3306                 */
3307                 s->queue_full_time_code ();
3308                 s->queue_song_position_pointer ();
3309         }
3310
3311         show_verbose_cursor_time (t);
3312         _editor->UpdateAllTransportClocks (t);
3313 }
3314
3315 void
3316 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3317 {
3318         Drag::start_grab (event, c);
3319         setup_snap_delta (_editor->playhead_cursor->current_frame ());
3320
3321         _grab_zoom = _editor->samples_per_pixel;
3322
3323         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3324
3325         _editor->snap_to_with_modifier (where, event);
3326
3327         _editor->_dragging_playhead = true;
3328
3329         Session* s = _editor->session ();
3330
3331         /* grab the track canvas item as well */
3332
3333         _cursor.track_canvas_item().grab();
3334
3335         if (s) {
3336                 if (_was_rolling && _stop) {
3337                         s->request_stop ();
3338                 }
3339
3340                 if (s->is_auditioning()) {
3341                         s->cancel_audition ();
3342                 }
3343
3344
3345                 if (AudioEngine::instance()->connected()) {
3346
3347                         /* do this only if we're the engine is connected
3348                          * because otherwise this request will never be
3349                          * serviced and we'll busy wait forever. likewise,
3350                          * notice if we are disconnected while waiting for the
3351                          * request to be serviced.
3352                          */
3353
3354                         s->request_suspend_timecode_transmission ();
3355                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3356                                 /* twiddle our thumbs */
3357                         }
3358                 }
3359         }
3360
3361         fake_locate (where - snap_delta (event->button.state));
3362 }
3363
3364 void
3365 CursorDrag::motion (GdkEvent* event, bool)
3366 {
3367         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3368         _editor->snap_to_with_modifier (where, event);
3369         if (where != last_pointer_frame()) {
3370                 fake_locate (where - snap_delta (event->button.state));
3371         }
3372 }
3373
3374 void
3375 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3376 {
3377         _editor->_dragging_playhead = false;
3378
3379         _cursor.track_canvas_item().ungrab();
3380
3381         if (!movement_occurred && _stop) {
3382                 return;
3383         }
3384
3385         motion (event, false);
3386
3387         Session* s = _editor->session ();
3388         if (s) {
3389                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3390                 _editor->_pending_locate_request = true;
3391                 s->request_resume_timecode_transmission ();
3392         }
3393 }
3394
3395 void
3396 CursorDrag::aborted (bool)
3397 {
3398         _cursor.track_canvas_item().ungrab();
3399
3400         if (_editor->_dragging_playhead) {
3401                 _editor->session()->request_resume_timecode_transmission ();
3402                 _editor->_dragging_playhead = false;
3403         }
3404
3405         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3406 }
3407
3408 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3409         : RegionDrag (e, i, p, v)
3410 {
3411         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3412 }
3413
3414 void
3415 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3416 {
3417         Drag::start_grab (event, cursor);
3418
3419         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3420         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3421         setup_snap_delta (r->position ());
3422
3423         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3424 }
3425
3426 void
3427 FadeInDrag::setup_pointer_frame_offset ()
3428 {
3429         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3430         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3431         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3432 }
3433
3434 void
3435 FadeInDrag::motion (GdkEvent* event, bool)
3436 {
3437         framecnt_t fade_length;
3438
3439         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3440         _editor->snap_to_with_modifier (pos, event);
3441         pos -= snap_delta (event->button.state);
3442
3443         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3444
3445         if (pos < (region->position() + 64)) {
3446                 fade_length = 64; // this should be a minimum defined somewhere
3447         } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3448                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3449         } else {
3450                 fade_length = pos - region->position();
3451         }
3452
3453         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3454
3455                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3456
3457                 if (!tmp) {
3458                         continue;
3459                 }
3460
3461                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3462         }
3463
3464         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3465 }
3466
3467 void
3468 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3469 {
3470         if (!movement_occurred) {
3471                 return;
3472         }
3473
3474         framecnt_t fade_length;
3475         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3476         _editor->snap_to_with_modifier (pos, event);
3477         pos -= snap_delta (event->button.state);
3478
3479         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3480
3481         if (pos < (region->position() + 64)) {
3482                 fade_length = 64; // this should be a minimum defined somewhere
3483         } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3484                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3485         } else {
3486                 fade_length = pos - region->position();
3487         }
3488
3489         bool in_command = false;
3490
3491         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3492
3493                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3494
3495                 if (!tmp) {
3496                         continue;
3497                 }
3498
3499                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3500                 XMLNode &before = alist->get_state();
3501
3502                 tmp->audio_region()->set_fade_in_length (fade_length);
3503                 tmp->audio_region()->set_fade_in_active (true);
3504
3505                 if (!in_command) {
3506                         _editor->begin_reversible_command (_("change fade in length"));
3507                         in_command = true;
3508                 }
3509                 XMLNode &after = alist->get_state();
3510                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3511         }
3512
3513         if (in_command) {
3514                 _editor->commit_reversible_command ();
3515         }
3516 }
3517
3518 void
3519 FadeInDrag::aborted (bool)
3520 {
3521         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3522                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3523
3524                 if (!tmp) {
3525                         continue;
3526                 }
3527
3528                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3529         }
3530 }
3531
3532 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3533         : RegionDrag (e, i, p, v)
3534 {
3535         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3536 }
3537
3538 void
3539 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3540 {
3541         Drag::start_grab (event, cursor);
3542
3543         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3544         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3545         setup_snap_delta (r->last_frame ());
3546
3547         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3548 }
3549
3550 void
3551 FadeOutDrag::setup_pointer_frame_offset ()
3552 {
3553         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3554         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3555         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3556 }
3557
3558 void
3559 FadeOutDrag::motion (GdkEvent* event, bool)
3560 {
3561         framecnt_t fade_length;
3562
3563         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3564         _editor->snap_to_with_modifier (pos, event);
3565         pos -= snap_delta (event->button.state);
3566
3567         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3568
3569         if (pos > (region->last_frame() - 64)) {
3570                 fade_length = 64; // this should really be a minimum fade defined somewhere
3571         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3572                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3573         } else {
3574                 fade_length = region->last_frame() - pos;
3575         }
3576
3577         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3578
3579                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3580
3581                 if (!tmp) {
3582                         continue;
3583                 }
3584
3585                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3586         }
3587
3588         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3589 }
3590
3591 void
3592 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3593 {
3594         if (!movement_occurred) {
3595                 return;
3596         }
3597
3598         framecnt_t fade_length;
3599
3600         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3601         _editor->snap_to_with_modifier (pos, event);
3602         pos -= snap_delta (event->button.state);
3603
3604         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3605
3606         if (pos > (region->last_frame() - 64)) {
3607                 fade_length = 64; // this should really be a minimum fade defined somewhere
3608         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3609                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3610         } else {
3611                 fade_length = region->last_frame() - pos;
3612         }
3613
3614         bool in_command = false;
3615
3616         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3617
3618                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3619
3620                 if (!tmp) {
3621                         continue;
3622                 }
3623
3624                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3625                 XMLNode &before = alist->get_state();
3626
3627                 tmp->audio_region()->set_fade_out_length (fade_length);
3628                 tmp->audio_region()->set_fade_out_active (true);
3629
3630                 if (!in_command) {
3631                         _editor->begin_reversible_command (_("change fade out length"));
3632                         in_command = false;
3633                 }
3634                 XMLNode &after = alist->get_state();
3635                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3636         }
3637
3638         if (in_command) {
3639                 _editor->commit_reversible_command ();
3640         }
3641 }
3642
3643 void
3644 FadeOutDrag::aborted (bool)
3645 {
3646         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3647                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3648
3649                 if (!tmp) {
3650                         continue;
3651                 }
3652
3653                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3654         }
3655 }
3656
3657 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3658         : Drag (e, i)
3659 {
3660         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3661
3662         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3663         assert (_marker);
3664
3665         _points.push_back (ArdourCanvas::Duple (0, 0));
3666         _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3667 }
3668
3669 MarkerDrag::~MarkerDrag ()
3670 {
3671         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3672                 delete i->location;
3673         }
3674 }
3675
3676 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3677 {
3678         location = new Location (*l);
3679         markers.push_back (m);
3680         move_both = false;
3681 }
3682
3683 void
3684 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3685 {
3686         Drag::start_grab (event, cursor);
3687
3688         bool is_start;
3689
3690         Location *location = _editor->find_location_from_marker (_marker, is_start);
3691         _editor->_dragging_edit_point = true;
3692
3693         update_item (location);
3694
3695         // _drag_line->show();
3696         // _line->raise_to_top();
3697
3698         if (is_start) {
3699                 show_verbose_cursor_time (location->start());
3700         } else {
3701                 show_verbose_cursor_time (location->end());
3702         }
3703
3704         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3705
3706         switch (op) {
3707         case Selection::Toggle:
3708                 /* we toggle on the button release */
3709                 break;
3710         case Selection::Set:
3711                 if (!_editor->selection->selected (_marker)) {
3712                         _editor->selection->set (_marker);
3713                 }
3714                 break;
3715         case Selection::Extend:
3716         {
3717                 Locations::LocationList ll;
3718                 list<Marker*> to_add;
3719                 framepos_t s, e;
3720                 _editor->selection->markers.range (s, e);
3721                 s = min (_marker->position(), s);
3722                 e = max (_marker->position(), e);
3723                 s = min (s, e);
3724                 e = max (s, e);
3725                 if (e < max_framepos) {
3726                         ++e;
3727                 }
3728                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3729                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3730                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3731                         if (lm) {
3732                                 if (lm->start) {
3733                                         to_add.push_back (lm->start);
3734                                 }
3735                                 if (lm->end) {
3736                                         to_add.push_back (lm->end);
3737                                 }
3738                         }
3739                 }
3740                 if (!to_add.empty()) {
3741                         _editor->selection->add (to_add);
3742                 }
3743                 break;
3744         }
3745         case Selection::Add:
3746                 _editor->selection->add (_marker);
3747                 break;
3748         }
3749
3750         /* Set up copies for us to manipulate during the drag
3751          */
3752
3753         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3754
3755                 Location* l = _editor->find_location_from_marker (*i, is_start);
3756
3757                 if (!l) {
3758                         continue;
3759                 }
3760
3761                 if (l->is_mark()) {
3762                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3763                 } else {
3764                         /* range: check that the other end of the range isn't
3765                            already there.
3766                         */
3767                         CopiedLocationInfo::iterator x;
3768                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3769                                 if (*(*x).location == *l) {
3770                                         break;
3771                                 }
3772                         }
3773                         if (x == _copied_locations.end()) {
3774                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3775                         } else {
3776                                 (*x).markers.push_back (*i);
3777                                 (*x).move_both = true;
3778                         }
3779                 }
3780
3781         }
3782 }
3783
3784 void
3785 MarkerDrag::setup_pointer_frame_offset ()
3786 {
3787         bool is_start;
3788         Location *location = _editor->find_location_from_marker (_marker, is_start);
3789         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3790 }
3791
3792 void
3793 MarkerDrag::motion (GdkEvent* event, bool)
3794 {
3795         framecnt_t f_delta = 0;
3796         bool is_start;
3797         bool move_both = false;
3798         Location *real_location;
3799         Location *copy_location = 0;
3800
3801         framepos_t const newframe = adjusted_current_frame (event);
3802         framepos_t next = newframe;
3803
3804         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3805                 move_both = true;
3806         }
3807
3808         CopiedLocationInfo::iterator x;
3809
3810         /* find the marker we're dragging, and compute the delta */
3811
3812         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3813
3814                 copy_location = (*x).location;
3815
3816                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3817
3818                         /* this marker is represented by this
3819                          * CopiedLocationMarkerInfo
3820                          */
3821
3822                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3823                                 /* que pasa ?? */
3824                                 return;
3825                         }
3826
3827                         if (real_location->is_mark()) {
3828                                 f_delta = newframe - copy_location->start();
3829                         } else {
3830
3831
3832                                 switch (_marker->type()) {
3833                                 case Marker::SessionStart:
3834                                 case Marker::RangeStart:
3835                                 case Marker::LoopStart:
3836                                 case Marker::PunchIn:
3837                                         f_delta = newframe - copy_location->start();
3838                                         break;
3839
3840                                 case Marker::SessionEnd:
3841                                 case Marker::RangeEnd:
3842                                 case Marker::LoopEnd:
3843                                 case Marker::PunchOut:
3844                                         f_delta = newframe - copy_location->end();
3845                                         break;
3846                                 default:
3847                                         /* what kind of marker is this ? */
3848                                         return;
3849                                 }
3850                         }
3851
3852                         break;
3853                 }
3854         }
3855
3856         if (x == _copied_locations.end()) {
3857                 /* hmm, impossible - we didn't find the dragged marker */
3858                 return;
3859         }
3860
3861         /* now move them all */
3862
3863         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3864
3865                 copy_location = x->location;
3866
3867                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3868                         continue;
3869                 }
3870
3871                 if (real_location->locked()) {
3872                         continue;
3873                 }
3874
3875                 if (copy_location->is_mark()) {
3876
3877                         /* now move it */
3878
3879                         copy_location->set_start (copy_location->start() + f_delta);
3880
3881                 } else {
3882
3883                         framepos_t new_start = copy_location->start() + f_delta;
3884                         framepos_t new_end = copy_location->end() + f_delta;
3885
3886                         if (is_start) { // start-of-range marker
3887
3888                                 if (move_both || (*x).move_both) {
3889                                         copy_location->set_start (new_start);
3890                                         copy_location->set_end (new_end);
3891                                 } else  if (new_start < copy_location->end()) {
3892                                         copy_location->set_start (new_start);
3893                                 } else if (newframe > 0) {
3894                                         _editor->snap_to (next, RoundUpAlways, true);
3895                                         copy_location->set_end (next);
3896                                         copy_location->set_start (newframe);
3897                                 }
3898
3899                         } else { // end marker
3900
3901                                 if (move_both || (*x).move_both) {
3902                                         copy_location->set_end (new_end);
3903                                         copy_location->set_start (new_start);
3904                                 } else if (new_end > copy_location->start()) {
3905                                         copy_location->set_end (new_end);
3906                                 } else if (newframe > 0) {
3907                                         _editor->snap_to (next, RoundDownAlways, true);
3908                                         copy_location->set_start (next);
3909                                         copy_location->set_end (newframe);
3910                                 }
3911                         }
3912                 }
3913
3914                 update_item (copy_location);
3915
3916                 /* now lookup the actual GUI items used to display this
3917                  * location and move them to wherever the copy of the location
3918                  * is now. This means that the logic in ARDOUR::Location is
3919                  * still enforced, even though we are not (yet) modifying
3920                  * the real Location itself.
3921                  */
3922
3923                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3924
3925                 if (lm) {
3926                         lm->set_position (copy_location->start(), copy_location->end());
3927                 }
3928
3929         }
3930
3931         assert (!_copied_locations.empty());
3932
3933         show_verbose_cursor_time (newframe);
3934 }
3935
3936 void
3937 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3938 {
3939         if (!movement_occurred) {
3940
3941                 if (was_double_click()) {
3942                         _editor->rename_marker (_marker);
3943                         return;
3944                 }
3945
3946                 /* just a click, do nothing but finish
3947                    off the selection process
3948                 */
3949
3950                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3951
3952                 switch (op) {
3953                 case Selection::Set:
3954                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3955                                 _editor->selection->set (_marker);
3956                         }
3957                         break;
3958
3959                 case Selection::Toggle:
3960                         /* we toggle on the button release, click only */
3961                         _editor->selection->toggle (_marker);
3962                         break;
3963
3964                 case Selection::Extend:
3965                 case Selection::Add:
3966                         break;
3967                 }
3968
3969                 return;
3970         }
3971
3972         _editor->_dragging_edit_point = false;
3973
3974         XMLNode &before = _editor->session()->locations()->get_state();
3975         bool in_command = false;
3976
3977         MarkerSelection::iterator i;
3978         CopiedLocationInfo::iterator x;
3979         bool is_start;
3980
3981         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3982              x != _copied_locations.end() && i != _editor->selection->markers.end();
3983              ++i, ++x) {
3984
3985                 Location * location = _editor->find_location_from_marker (*i, is_start);
3986
3987                 if (location) {
3988
3989                         if (location->locked()) {
3990                                 continue;
3991                         }
3992                         if (!in_command) {
3993                                 _editor->begin_reversible_command ( _("move marker") );
3994                                 in_command = true;
3995                         }
3996                         if (location->is_mark()) {
3997                                 location->set_start (((*x).location)->start());
3998                         } else {
3999                                 location->set (((*x).location)->start(), ((*x).location)->end());
4000                         }
4001                 }
4002         }
4003
4004         if (in_command) {
4005                 XMLNode &after = _editor->session()->locations()->get_state();
4006                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4007                 _editor->commit_reversible_command ();
4008         }
4009 }
4010
4011 void
4012 MarkerDrag::aborted (bool movement_occured)
4013 {
4014         if (!movement_occured) {
4015                 return;
4016         }
4017
4018         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4019
4020                 /* move all markers to their original location */
4021
4022
4023                 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4024
4025                         bool is_start;
4026                         Location * location = _editor->find_location_from_marker (*m, is_start);
4027
4028                         if (location) {
4029                                 (*m)->set_position (is_start ? location->start() : location->end());
4030                         }
4031                 }
4032         }
4033 }
4034
4035 void
4036 MarkerDrag::update_item (Location*)
4037 {
4038         /* noop */
4039 }
4040
4041 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4042         : Drag (e, i),
4043           _cumulative_x_drag (0),
4044           _cumulative_y_drag (0)
4045 {
4046         if (_zero_gain_fraction < 0.0) {
4047                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4048         }
4049
4050         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4051
4052         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4053         assert (_point);
4054 }
4055
4056
4057 void
4058 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4059 {
4060         Drag::start_grab (event, _editor->cursors()->fader);
4061
4062         // start the grab at the center of the control point so
4063         // the point doesn't 'jump' to the mouse after the first drag
4064         _fixed_grab_x = _point->get_x();
4065         _fixed_grab_y = _point->get_y();
4066
4067         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4068         setup_snap_delta (pos);
4069
4070         float const fraction = 1 - (_point->get_y() / _point->line().height());
4071         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4072
4073         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4074
4075         if (!_point->can_slide ()) {
4076                 _x_constrained = true;
4077         }
4078 }
4079
4080 void
4081 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4082 {
4083         double dx = _drags->current_pointer_x() - last_pointer_x();
4084         double dy = current_pointer_y() - last_pointer_y();
4085
4086         if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4087                 dx *= 0.1;
4088                 dy *= 0.1;
4089         }
4090
4091         /* coordinate in pixels relative to the start of the region (for region-based automation)
4092            or track (for track-based automation) */
4093         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4094         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4095
4096         // calculate zero crossing point. back off by .01 to stay on the
4097         // positive side of zero
4098         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4099
4100         if (_x_constrained) {
4101                 cx = _fixed_grab_x;
4102         }
4103         if (_y_constrained) {
4104                 cy = _fixed_grab_y;
4105         }
4106
4107         _cumulative_x_drag = cx - _fixed_grab_x;
4108         _cumulative_y_drag = cy - _fixed_grab_y;
4109
4110         // make sure we hit zero when passing through
4111         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4112                 cy = zero_gain_y;
4113         }
4114
4115         cx = max (0.0, cx);
4116         cy = max (0.0, cy);
4117         cy = min ((double) _point->line().height(), cy);
4118
4119         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4120
4121         if (!_x_constrained) {
4122                 _editor->snap_to_with_modifier (cx_frames, event);
4123         }
4124
4125         cx_frames -= snap_delta (event->button.state);
4126         cx_frames = min (cx_frames, _point->line().maximum_time());
4127
4128         float const fraction = 1.0 - (cy / _point->line().height());
4129
4130         if (first_motion) {
4131                 _editor->begin_reversible_command (_("automation event move"));
4132                 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4133         }
4134
4135         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4136
4137         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4138 }
4139
4140 void
4141 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4142 {
4143         if (!movement_occurred) {
4144
4145                 /* just a click */
4146                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4147                         _editor->reset_point_selection ();
4148                 }
4149
4150         } else {
4151                 motion (event, false);
4152                 _point->line().end_drag (_pushing, _final_index);
4153                 _editor->commit_reversible_command ();
4154         }
4155 }
4156
4157 void
4158 ControlPointDrag::aborted (bool)
4159 {
4160         _point->line().reset ();
4161 }
4162
4163 bool
4164 ControlPointDrag::active (Editing::MouseMode m)
4165 {
4166         if (m == Editing::MouseDraw) {
4167                 /* always active in mouse draw */
4168                 return true;
4169         }
4170
4171         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4172         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4173 }
4174
4175 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4176         : Drag (e, i)
4177         , _line (0)
4178         , _cumulative_y_drag (0)
4179         , _before (0)
4180         , _after (0)
4181 {
4182         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4183 }
4184
4185 void
4186 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4187 {
4188         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4189         assert (_line);
4190
4191         _item = &_line->grab_item ();
4192
4193         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4194            origin, and ditto for y.
4195         */
4196
4197         double cx = event->button.x;
4198         double cy = event->button.y;
4199
4200         _line->parent_group().canvas_to_item (cx, cy);
4201
4202         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4203
4204         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4205                 /* no adjacent points */
4206                 return;
4207         }
4208
4209         Drag::start_grab (event, _editor->cursors()->fader);
4210
4211         /* store grab start in parent frame */
4212
4213         _fixed_grab_x = cx;
4214         _fixed_grab_y = cy;
4215
4216         double fraction = 1.0 - (cy / _line->height());
4217
4218         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4219 }
4220
4221 void
4222 LineDrag::motion (GdkEvent* event, bool first_move)
4223 {
4224         double dy = current_pointer_y() - last_pointer_y();
4225
4226         if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4227                 dy *= 0.1;
4228         }
4229
4230         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4231
4232         _cumulative_y_drag = cy - _fixed_grab_y;
4233
4234         cy = max (0.0, cy);
4235         cy = min ((double) _line->height(), cy);
4236
4237         double const fraction = 1.0 - (cy / _line->height());
4238         uint32_t ignored;
4239
4240         if (first_move) {
4241                 _editor->begin_reversible_command (_("automation range move"));
4242                 _line->start_drag_line (_before, _after, fraction);
4243         }
4244
4245         /* we are ignoring x position for this drag, so we can just pass in anything */
4246         _line->drag_motion (0, fraction, true, false, ignored);
4247
4248         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4249 }
4250
4251 void
4252 LineDrag::finished (GdkEvent* event, bool movement_occured)
4253 {
4254         if (movement_occured) {
4255                 motion (event, false);
4256                 _line->end_drag (false, 0);
4257                 _editor->commit_reversible_command ();
4258         } else {
4259                 /* add a new control point on the line */
4260
4261                 AutomationTimeAxisView* atv;
4262
4263                 _line->end_drag (false, 0);
4264
4265                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4266                         framepos_t where = _editor->window_event_sample (event, 0, 0);
4267                         atv->add_automation_event (event, where, event->button.y, false);
4268                 }
4269         }
4270 }
4271
4272 void
4273 LineDrag::aborted (bool)
4274 {
4275         _line->reset ();
4276 }
4277
4278 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4279         : Drag (e, i),
4280           _line (0),
4281           _cumulative_x_drag (0)
4282 {
4283         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4284 }
4285
4286 void
4287 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4288 {
4289         Drag::start_grab (event);
4290
4291         _line = reinterpret_cast<Line*> (_item);
4292         assert (_line);
4293
4294         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4295
4296         double cx = event->button.x;
4297         double cy = event->button.y;
4298
4299         _item->parent()->canvas_to_item (cx, cy);
4300
4301         /* store grab start in parent frame */
4302         _region_view_grab_x = cx;
4303
4304         _before = *(float*) _item->get_data ("position");
4305
4306         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4307
4308         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4309 }
4310
4311 void
4312 FeatureLineDrag::motion (GdkEvent*, bool)
4313 {
4314         double dx = _drags->current_pointer_x() - last_pointer_x();
4315
4316         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4317
4318         _cumulative_x_drag += dx;
4319
4320         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4321
4322         if (cx > _max_x){
4323                 cx = _max_x;
4324         }
4325         else if(cx < 0){
4326                 cx = 0;
4327         }
4328
4329         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4330         assert (bbox);
4331         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4332
4333         float *pos = new float;
4334         *pos = cx;
4335
4336         _line->set_data ("position", pos);
4337
4338         _before = cx;
4339 }
4340
4341 void
4342 FeatureLineDrag::finished (GdkEvent*, bool)
4343 {
4344         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4345         _arv->update_transient(_before, _before);
4346 }
4347
4348 void
4349 FeatureLineDrag::aborted (bool)
4350 {
4351         //_line->reset ();
4352 }
4353
4354 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4355         : Drag (e, i)
4356         , _vertical_only (false)
4357 {
4358         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4359 }
4360
4361 void
4362 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4363 {
4364         Drag::start_grab (event);
4365         show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
4366 }
4367
4368 void
4369 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4370 {
4371         framepos_t start;
4372         framepos_t end;
4373         double y1;
4374         double y2;
4375
4376         framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
4377
4378         framepos_t grab = grab_frame ();
4379         if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4380                 _editor->snap_to_with_modifier (grab, event);
4381         } else {
4382                 grab = raw_grab_frame ();
4383         }
4384
4385         /* base start and end on initial click position */
4386
4387         if (pf < grab) {
4388                 start = pf;
4389                 end = grab;
4390         } else {
4391                 end = pf;
4392                 start = grab;
4393         }
4394
4395         if (current_pointer_y() < grab_y()) {
4396                 y1 = current_pointer_y();
4397                 y2 = grab_y();
4398         } else {
4399                 y2 = current_pointer_y();
4400                 y1 = grab_y();
4401         }
4402
4403         if (start != end || y1 != y2) {
4404
4405                 double x1 = _editor->sample_to_pixel (start);
4406                 double x2 = _editor->sample_to_pixel (end);
4407                 const double min_dimension = 2.0;
4408
4409                 if (_vertical_only) {
4410                         /* fixed 10 pixel width */
4411                         x2 = x1 + 10;
4412                 } else {
4413                         if (x2 < x1) {
4414                                 x2 = min (x1 - min_dimension, x2);
4415                         } else {
4416                                 x2 = max (x1 + min_dimension, x2);
4417                         }
4418                 }
4419
4420                 if (y2 < y1) {
4421                         y2 = min (y1 - min_dimension, y2);
4422                 } else {
4423                         y2 = max (y1 + min_dimension, y2);
4424                 }
4425
4426                 /* translate rect into item space and set */
4427
4428                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4429
4430                 /* this drag is a _trackview_only == true drag, so the y1 and
4431                  * y2 (computed using current_pointer_y() and grab_y()) will be
4432                  * relative to the top of the trackview group). The
4433                  * rubberband rect has the same parent/scroll offset as the
4434                  * the trackview group, so we can use the "r" rect directly
4435                  * to set the shape of the rubberband.
4436                  */
4437
4438                 _editor->rubberband_rect->set (r);
4439                 _editor->rubberband_rect->show();
4440                 _editor->rubberband_rect->raise_to_top();
4441
4442                 show_verbose_cursor_time (pf);
4443
4444                 do_select_things (event, true);
4445         }
4446 }
4447
4448 void
4449 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4450 {
4451         framepos_t x1;
4452         framepos_t x2;
4453         framepos_t grab = grab_frame ();
4454         framepos_t lpf = last_pointer_frame ();
4455
4456         if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
4457                 grab = raw_grab_frame ();
4458                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4459         }
4460
4461         if (grab < lpf) {
4462                 x1 = grab;
4463                 x2 = lpf;
4464         } else {
4465                 x2 = grab;
4466                 x1 = lpf;
4467         }
4468
4469         double y1;
4470         double y2;
4471
4472         if (current_pointer_y() < grab_y()) {
4473                 y1 = current_pointer_y();
4474                 y2 = grab_y();
4475         } else {
4476                 y2 = current_pointer_y();
4477                 y1 = grab_y();
4478         }
4479
4480         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4481 }
4482
4483 void
4484 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4485 {
4486         if (movement_occurred) {
4487
4488                 motion (event, false);
4489                 do_select_things (event, false);
4490
4491         } else {
4492
4493                 /* just a click */
4494
4495                 bool do_deselect = true;
4496                 MidiTimeAxisView* mtv;
4497
4498                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4499                         /* MIDI track */
4500                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4501                                 /* nothing selected */
4502                                 add_midi_region (mtv);
4503                                 do_deselect = false;
4504                         }
4505                 }
4506
4507                 /* do not deselect if Primary or Tertiary (toggle-select or
4508                  * extend-select are pressed.
4509                  */
4510
4511                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4512                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4513                     do_deselect) {
4514                         deselect_things ();
4515                 }
4516
4517         }
4518
4519         _editor->rubberband_rect->hide();
4520 }
4521
4522 void
4523 RubberbandSelectDrag::aborted (bool)
4524 {
4525         _editor->rubberband_rect->hide ();
4526 }
4527
4528 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4529         : RegionDrag (e, i, p, v)
4530 {
4531         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4532 }
4533
4534 void
4535 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4536 {
4537         Drag::start_grab (event, cursor);
4538
4539         _editor->get_selection().add (_primary);
4540
4541         framepos_t where = _primary->region()->position();
4542         setup_snap_delta (where);
4543
4544         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4545 }
4546
4547 void
4548 TimeFXDrag::motion (GdkEvent* event, bool)
4549 {
4550         RegionView* rv = _primary;
4551         StreamView* cv = rv->get_time_axis_view().view ();
4552
4553         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4554         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4555         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4556         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4557         _editor->snap_to_with_modifier (pf, event);
4558         pf -= snap_delta (event->button.state);
4559
4560         if (pf > rv->region()->position()) {
4561                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4562         }
4563
4564         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4565 }
4566
4567 void
4568 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4569 {
4570         _primary->get_time_axis_view().hide_timestretch ();
4571
4572         if (!movement_occurred) {
4573                 return;
4574         }
4575
4576         if (last_pointer_frame() < _primary->region()->position()) {
4577                 /* backwards drag of the left edge - not usable */
4578                 return;
4579         }
4580
4581         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4582
4583         float percentage = (double) newlen / (double) _primary->region()->length();
4584
4585 #ifndef USE_RUBBERBAND
4586         // Soundtouch uses percentage / 100 instead of normal (/ 1)
4587         if (_primary->region()->data_type() == DataType::AUDIO) {
4588                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4589         }
4590 #endif
4591
4592         if (!_editor->get_selection().regions.empty()) {
4593                 /* primary will already be included in the selection, and edit
4594                    group shared editing will propagate selection across
4595                    equivalent regions, so just use the current region
4596                    selection.
4597                 */
4598
4599                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4600                         error << _("An error occurred while executing time stretch operation") << endmsg;
4601                 }
4602         }
4603 }
4604
4605 void
4606 TimeFXDrag::aborted (bool)
4607 {
4608         _primary->get_time_axis_view().hide_timestretch ();
4609 }
4610
4611 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4612         : Drag (e, i)
4613 {
4614         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4615 }
4616
4617 void
4618 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4619 {
4620         Drag::start_grab (event);
4621 }
4622
4623 void
4624 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4625 {
4626         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4627 }
4628
4629 void
4630 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4631 {
4632         if (movement_occurred && _editor->session()) {
4633                 /* make sure we stop */
4634                 _editor->session()->request_transport_speed (0.0);
4635         }
4636 }
4637
4638 void
4639 ScrubDrag::aborted (bool)
4640 {
4641         /* XXX: TODO */
4642 }
4643
4644 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4645         : Drag (e, i)
4646         , _operation (o)
4647         , _add (false)
4648         , _time_selection_at_start (!_editor->get_selection().time.empty())
4649 {
4650         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4651
4652         if (_time_selection_at_start) {
4653                 start_at_start = _editor->get_selection().time.start();
4654                 end_at_start = _editor->get_selection().time.end_frame();
4655         }
4656 }
4657
4658 void
4659 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4660 {
4661         if (_editor->session() == 0) {
4662                 return;
4663         }
4664
4665         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4666
4667         switch (_operation) {
4668         case CreateSelection:
4669                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4670                         _add = true;
4671                 } else {
4672                         _add = false;
4673                 }
4674                 cursor = _editor->cursors()->selector;
4675                 Drag::start_grab (event, cursor);
4676                 break;
4677
4678         case SelectionStartTrim:
4679                 if (_editor->clicked_axisview) {
4680                         _editor->clicked_axisview->order_selection_trims (_item, true);
4681                 }
4682                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4683                 break;
4684
4685         case SelectionEndTrim:
4686                 if (_editor->clicked_axisview) {
4687                         _editor->clicked_axisview->order_selection_trims (_item, false);
4688                 }
4689                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4690                 break;
4691
4692         case SelectionMove:
4693                 Drag::start_grab (event, cursor);
4694                 break;
4695
4696         case SelectionExtend:
4697                 Drag::start_grab (event, cursor);
4698                 break;
4699         }
4700
4701         if (_operation == SelectionMove) {
4702                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4703         } else {
4704                 show_verbose_cursor_time (adjusted_current_frame (event));
4705         }
4706 }
4707
4708 void
4709 SelectionDrag::setup_pointer_frame_offset ()
4710 {
4711         switch (_operation) {
4712         case CreateSelection:
4713                 _pointer_frame_offset = 0;
4714                 break;
4715
4716         case SelectionStartTrim:
4717         case SelectionMove:
4718                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4719                 break;
4720
4721         case SelectionEndTrim:
4722                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4723                 break;
4724
4725         case SelectionExtend:
4726                 break;
4727         }
4728 }
4729
4730 void
4731 SelectionDrag::motion (GdkEvent* event, bool first_move)
4732 {
4733         framepos_t start = 0;
4734         framepos_t end = 0;
4735         framecnt_t length = 0;
4736         framecnt_t distance = 0;
4737
4738         framepos_t const pending_position = adjusted_current_frame (event);
4739
4740         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4741                 return;
4742         }
4743
4744         switch (_operation) {
4745         case CreateSelection:
4746         {
4747                 framepos_t grab = grab_frame ();
4748
4749                 if (first_move) {
4750                         grab = adjusted_current_frame (event, false);
4751                         if (grab < pending_position) {
4752                                 _editor->snap_to (grab, RoundDownMaybe);
4753                         }  else {
4754                                 _editor->snap_to (grab, RoundUpMaybe);
4755                         }
4756                 }
4757
4758                 if (pending_position < grab) {
4759                         start = pending_position;
4760                         end = grab;
4761                 } else {
4762                         end = pending_position;
4763                         start = grab;
4764                 }
4765
4766                 /* first drag: Either add to the selection
4767                    or create a new selection
4768                 */
4769
4770                 if (first_move) {
4771
4772                         if (_add) {
4773
4774                                 /* adding to the selection */
4775                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4776                                 _editor->clicked_selection = _editor->selection->add (start, end);
4777                                 _add = false;
4778
4779                         } else {
4780
4781                                 /* new selection */
4782
4783                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4784                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4785                                 }
4786
4787                                 _editor->clicked_selection = _editor->selection->set (start, end);
4788                         }
4789                 }
4790
4791                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4792                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4793                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4794                 if (atest) {
4795                         _editor->selection->add (atest);
4796                         break;
4797                 }
4798
4799                 /* select all tracks within the rectangle that we've marked out so far */
4800                 TrackViewList new_selection;
4801                 TrackViewList& all_tracks (_editor->track_views);
4802
4803                 ArdourCanvas::Coord const top = grab_y();
4804                 ArdourCanvas::Coord const bottom = current_pointer_y();
4805
4806                 if (top >= 0 && bottom >= 0) {
4807
4808                         //first, find the tracks that are covered in the y range selection
4809                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4810                                 if ((*i)->covered_by_y_range (top, bottom)) {
4811                                         new_selection.push_back (*i);
4812                                 }
4813                         }
4814
4815                         //now find any tracks that are GROUPED with the tracks we selected
4816                         TrackViewList grouped_add = new_selection;
4817                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4818                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4819                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4820                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4821                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4822                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4823                                                         grouped_add.push_back (*j);
4824                                         }
4825                                 }
4826                         }
4827
4828                         //now compare our list with the current selection, and add or remove as necessary
4829                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4830                         TrackViewList tracks_to_add;
4831                         TrackViewList tracks_to_remove;
4832                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4833                                 if ( !_editor->selection->tracks.contains ( *i ) )
4834                                         tracks_to_add.push_back ( *i );
4835                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4836                                 if ( !grouped_add.contains ( *i ) )
4837                                         tracks_to_remove.push_back ( *i );
4838                         _editor->selection->add(tracks_to_add);
4839                         _editor->selection->remove(tracks_to_remove);
4840
4841                 }
4842         }
4843         break;
4844
4845         case SelectionStartTrim:
4846
4847                 start = _editor->selection->time[_editor->clicked_selection].start;
4848                 end = _editor->selection->time[_editor->clicked_selection].end;
4849
4850                 if (pending_position > end) {
4851                         start = end;
4852                 } else {
4853                         start = pending_position;
4854                 }
4855                 break;
4856
4857         case SelectionEndTrim:
4858
4859                 start = _editor->selection->time[_editor->clicked_selection].start;
4860                 end = _editor->selection->time[_editor->clicked_selection].end;
4861
4862                 if (pending_position < start) {
4863                         end = start;
4864                 } else {
4865                         end = pending_position;
4866                 }
4867
4868                 break;
4869
4870         case SelectionMove:
4871
4872                 start = _editor->selection->time[_editor->clicked_selection].start;
4873                 end = _editor->selection->time[_editor->clicked_selection].end;
4874
4875                 length = end - start;
4876                 distance = pending_position - start;
4877                 start = pending_position;
4878                 _editor->snap_to (start);
4879
4880                 end = start + length;
4881
4882                 break;
4883
4884         case SelectionExtend:
4885                 break;
4886         }
4887
4888         if (start != end) {
4889                 switch (_operation) {
4890                 case SelectionMove:
4891                         if (_time_selection_at_start) {
4892                                 _editor->selection->move_time (distance);
4893                         }
4894                         break;
4895                 default:
4896                         _editor->selection->replace (_editor->clicked_selection, start, end);
4897                 }
4898         }
4899
4900         if (_operation == SelectionMove) {
4901                 show_verbose_cursor_time(start);
4902         } else {
4903                 show_verbose_cursor_time(pending_position);
4904         }
4905 }
4906
4907 void
4908 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4909 {
4910         Session* s = _editor->session();
4911
4912         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4913         if (movement_occurred) {
4914                 motion (event, false);
4915                 /* XXX this is not object-oriented programming at all. ick */
4916                 if (_editor->selection->time.consolidate()) {
4917                         _editor->selection->TimeChanged ();
4918                 }
4919
4920                 /* XXX what if its a music time selection? */
4921                 if (s) {
4922                         if ( s->get_play_range() && s->transport_rolling() ) {
4923                                 s->request_play_range (&_editor->selection->time, true);
4924                         } else {
4925                                 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4926                                         if (_operation == SelectionEndTrim)
4927                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4928                                         else
4929                                                 s->request_locate (_editor->get_selection().time.start());
4930                                 }
4931                         }
4932                 }
4933
4934         } else {
4935                 /* just a click, no pointer movement.
4936                  */
4937
4938                 if (_operation == SelectionExtend) {
4939                         if (_time_selection_at_start) {
4940                                 framepos_t pos = adjusted_current_frame (event, false);
4941                                 framepos_t start = min (pos, start_at_start);
4942                                 framepos_t end = max (pos, end_at_start);
4943                                 _editor->selection->set (start, end);
4944                         }
4945                 } else {
4946                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4947                                 if (_editor->clicked_selection) {
4948                                         _editor->selection->remove (_editor->clicked_selection);
4949                                 }
4950                         } else {
4951                                 if (!_editor->clicked_selection) {
4952                                         _editor->selection->clear_time();
4953                                 }
4954                         }
4955                 }
4956
4957                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4958                         _editor->selection->set (_editor->clicked_axisview);
4959                 }
4960
4961                 if (s && s->get_play_range () && s->transport_rolling()) {
4962                         s->request_stop (false, false);
4963                 }
4964
4965         }
4966
4967         _editor->stop_canvas_autoscroll ();
4968         _editor->clicked_selection = 0;
4969         _editor->commit_reversible_selection_op ();
4970 }
4971
4972 void
4973 SelectionDrag::aborted (bool)
4974 {
4975         /* XXX: TODO */
4976 }
4977
4978 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4979         : Drag (e, i, false),
4980           _operation (o),
4981           _copy (false)
4982 {
4983         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4984
4985         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
4986                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
4987                                                                       physical_screen_height (_editor->get_window())));
4988         _drag_rect->hide ();
4989
4990         _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4991         _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4992 }
4993
4994 RangeMarkerBarDrag::~RangeMarkerBarDrag()
4995 {
4996         /* normal canvas items will be cleaned up when their parent group is deleted. But
4997            this item is created as the child of a long-lived parent group, and so we
4998            need to explicitly delete it.
4999         */
5000         delete _drag_rect;
5001 }
5002
5003 void
5004 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5005 {
5006         if (_editor->session() == 0) {
5007                 return;
5008         }
5009
5010         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5011
5012         if (!_editor->temp_location) {
5013                 _editor->temp_location = new Location (*_editor->session());
5014         }
5015
5016         switch (_operation) {
5017         case CreateSkipMarker:
5018         case CreateRangeMarker:
5019         case CreateTransportMarker:
5020         case CreateCDMarker:
5021
5022                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5023                         _copy = true;
5024                 } else {
5025                         _copy = false;
5026                 }
5027                 cursor = _editor->cursors()->selector;
5028                 break;
5029         }
5030
5031         Drag::start_grab (event, cursor);
5032
5033         show_verbose_cursor_time (adjusted_current_frame (event));
5034 }
5035
5036 void
5037 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5038 {
5039         framepos_t start = 0;
5040         framepos_t end = 0;
5041         ArdourCanvas::Rectangle *crect;
5042
5043         switch (_operation) {
5044         case CreateSkipMarker:
5045                 crect = _editor->range_bar_drag_rect;
5046                 break;
5047         case CreateRangeMarker:
5048                 crect = _editor->range_bar_drag_rect;
5049                 break;
5050         case CreateTransportMarker:
5051                 crect = _editor->transport_bar_drag_rect;
5052                 break;
5053         case CreateCDMarker:
5054                 crect = _editor->cd_marker_bar_drag_rect;
5055                 break;
5056         default:
5057                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5058                 return;
5059                 break;
5060         }
5061
5062         framepos_t const pf = adjusted_current_frame (event);
5063
5064         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5065                 framepos_t grab = grab_frame ();
5066                 _editor->snap_to (grab);
5067
5068                 if (pf < grab_frame()) {
5069                         start = pf;
5070                         end = grab;
5071                 } else {
5072                         end = pf;
5073                         start = grab;
5074                 }
5075
5076                 /* first drag: Either add to the selection
5077                    or create a new selection.
5078                 */
5079
5080                 if (first_move) {
5081
5082                         _editor->temp_location->set (start, end);
5083
5084                         crect->show ();
5085
5086                         update_item (_editor->temp_location);
5087                         _drag_rect->show();
5088                         //_drag_rect->raise_to_top();
5089
5090                 }
5091         }
5092
5093         if (start != end) {
5094                 _editor->temp_location->set (start, end);
5095
5096                 double x1 = _editor->sample_to_pixel (start);
5097                 double x2 = _editor->sample_to_pixel (end);
5098                 crect->set_x0 (x1);
5099                 crect->set_x1 (x2);
5100
5101                 update_item (_editor->temp_location);
5102         }
5103
5104         show_verbose_cursor_time (pf);
5105
5106 }
5107
5108 void
5109 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5110 {
5111         Location * newloc = 0;
5112         string rangename;
5113         int flags;
5114
5115         if (movement_occurred) {
5116                 motion (event, false);
5117                 _drag_rect->hide();
5118
5119                 switch (_operation) {
5120                 case CreateSkipMarker:
5121                 case CreateRangeMarker:
5122                 case CreateCDMarker:
5123                     {
5124                         XMLNode &before = _editor->session()->locations()->get_state();
5125                         if (_operation == CreateSkipMarker) {
5126                                 _editor->begin_reversible_command (_("new skip marker"));
5127                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5128                                 flags = Location::IsRangeMarker | Location::IsSkip;
5129                                 _editor->range_bar_drag_rect->hide();
5130                         } else if (_operation == CreateCDMarker) {
5131                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5132                                 _editor->begin_reversible_command (_("new CD marker"));
5133                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5134                                 _editor->cd_marker_bar_drag_rect->hide();
5135                         } else {
5136                                 _editor->begin_reversible_command (_("new skip marker"));
5137                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5138                                 flags = Location::IsRangeMarker;
5139                                 _editor->range_bar_drag_rect->hide();
5140                         }
5141                         newloc = new Location (
5142                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5143                                 );
5144
5145                         _editor->session()->locations()->add (newloc, true);
5146                         XMLNode &after = _editor->session()->locations()->get_state();
5147                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5148                         _editor->commit_reversible_command ();
5149                         break;
5150                     }
5151
5152                 case CreateTransportMarker:
5153                         // popup menu to pick loop or punch
5154                         _editor->new_transport_marker_context_menu (&event->button, _item);
5155                         break;
5156                 }
5157
5158         } else {
5159
5160                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5161
5162                 if (_operation == CreateTransportMarker) {
5163
5164                         /* didn't drag, so just locate */
5165
5166                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5167
5168                 } else if (_operation == CreateCDMarker) {
5169
5170                         /* didn't drag, but mark is already created so do
5171                          * nothing */
5172
5173                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5174
5175                         framepos_t start;
5176                         framepos_t end;
5177
5178                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5179
5180                         if (end == max_framepos) {
5181                                 end = _editor->session()->current_end_frame ();
5182                         }
5183
5184                         if (start == max_framepos) {
5185                                 start = _editor->session()->current_start_frame ();
5186                         }
5187
5188                         switch (_editor->mouse_mode) {
5189                         case MouseObject:
5190                                 /* find the two markers on either side and then make the selection from it */
5191                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5192                                 break;
5193
5194                         case MouseRange:
5195                                 /* find the two markers on either side of the click and make the range out of it */
5196                                 _editor->selection->set (start, end);
5197                                 break;
5198
5199                         default:
5200                                 break;
5201                         }
5202                 }
5203         }
5204
5205         _editor->stop_canvas_autoscroll ();
5206 }
5207
5208 void
5209 RangeMarkerBarDrag::aborted (bool movement_occured)
5210 {
5211         if (movement_occured) {
5212                 _drag_rect->hide ();
5213         }
5214 }
5215
5216 void
5217 RangeMarkerBarDrag::update_item (Location* location)
5218 {
5219         double const x1 = _editor->sample_to_pixel (location->start());
5220         double const x2 = _editor->sample_to_pixel (location->end());
5221
5222         _drag_rect->set_x0 (x1);
5223         _drag_rect->set_x1 (x2);
5224 }
5225
5226 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5227         : Drag (e, i)
5228         , _cumulative_dx (0)
5229         , _cumulative_dy (0)
5230 {
5231         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5232
5233         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5234         assert (_primary);
5235         _region = &_primary->region_view ();
5236         _note_height = _region->midi_stream_view()->note_height ();
5237 }
5238
5239 void
5240 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5241 {
5242         Drag::start_grab (event);
5243         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5244
5245         if (!(_was_selected = _primary->selected())) {
5246
5247                 /* tertiary-click means extend selection - we'll do that on button release,
5248                    so don't add it here, because otherwise we make it hard to figure
5249                    out the "extend-to" range.
5250                 */
5251
5252                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5253
5254                 if (!extend) {
5255                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5256
5257                         if (add) {
5258                                 _region->note_selected (_primary, true);
5259                         } else {
5260                                 _region->unique_select (_primary);
5261                         }
5262
5263                         _editor->begin_reversible_selection_op(X_("Select Note Press"));
5264                         _editor->commit_reversible_selection_op();
5265                 }
5266         }
5267 }
5268
5269 /** @return Current total drag x change in frames */
5270 frameoffset_t
5271 NoteDrag::total_dx (const guint state) const
5272 {
5273         /* dx in frames */
5274         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5275
5276         /* primary note time */
5277         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5278
5279         /* new time of the primary note in session frames */
5280         frameoffset_t st = n + dx + snap_delta (state);
5281
5282         framepos_t const rp = _region->region()->position ();
5283
5284         /* prevent the note being dragged earlier than the region's position */
5285         st = max (st, rp);
5286
5287         /* possibly snap and return corresponding delta */
5288
5289         bool snap = true;
5290
5291         if (ArdourKeyboard::indicates_snap (state)) {
5292                 if (_editor->snap_mode () != SnapOff) {
5293                         snap = false;
5294                 }
5295         } else {
5296                 if (_editor->snap_mode () == SnapOff) {
5297                         snap = false;
5298                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5299                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5300                                 snap = true;
5301                         }
5302                 }
5303         }
5304
5305         frameoffset_t ret;
5306         if (snap) {
5307                 ret =  _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5308         } else {
5309                 ret = st - n - snap_delta (state);
5310         }
5311         return ret;
5312 }
5313
5314 /** @return Current total drag y change in note number */
5315 int8_t
5316 NoteDrag::total_dy () const
5317 {
5318         MidiStreamView* msv = _region->midi_stream_view ();
5319         double const y = _region->midi_view()->y_position ();
5320         /* new current note */
5321         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5322         /* clamp */
5323         n = max (msv->lowest_note(), n);
5324         n = min (msv->highest_note(), n);
5325         /* and work out delta */
5326         return n - msv->y_to_note (grab_y() - y);
5327 }
5328
5329 void
5330 NoteDrag::motion (GdkEvent * event, bool)
5331 {
5332         /* Total change in x and y since the start of the drag */
5333         frameoffset_t const dx = total_dx (event->button.state);
5334         int8_t const dy = total_dy ();
5335
5336         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5337         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5338         double const tdy = -dy * _note_height - _cumulative_dy;
5339
5340         if (tdx || tdy) {
5341                 _cumulative_dx += tdx;
5342                 _cumulative_dy += tdy;
5343
5344                 int8_t note_delta = total_dy();
5345
5346                 _region->move_selection (tdx, tdy, note_delta);
5347
5348                 /* the new note value may be the same as the old one, but we
5349                  * don't know what that means because the selection may have
5350                  * involved more than one note and we might be doing something
5351                  * odd with them. so show the note value anyway, always.
5352                  */
5353
5354                 char buf[12];
5355                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5356
5357                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5358                           (int) floor ((double)new_note));
5359
5360                 show_verbose_cursor_text (buf);
5361         }
5362 }
5363
5364 void
5365 NoteDrag::finished (GdkEvent* ev, bool moved)
5366 {
5367         if (!moved) {
5368                 /* no motion - select note */
5369
5370                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
5371                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5372
5373                         bool changed = false;
5374
5375                         if (_was_selected) {
5376                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5377                                 if (add) {
5378                                         _region->note_deselected (_primary);
5379                                         changed = true;
5380                                 }
5381                         } else {
5382                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5383                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5384
5385                                 if (!extend && !add && _region->selection_size() > 1) {
5386                                         _region->unique_select (_primary);
5387                                         changed = true;
5388                                 } else if (extend) {
5389                                         _region->note_selected (_primary, true, true);
5390                                         changed = true;
5391                                 } else {
5392                                         /* it was added during button press */
5393                                 }
5394                         }
5395
5396                         if (changed) {
5397                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5398                                 _editor->commit_reversible_selection_op();
5399                         }
5400                 }
5401         } else {
5402                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5403         }
5404 }
5405
5406 void
5407 NoteDrag::aborted (bool)
5408 {
5409         /* XXX: TODO */
5410 }
5411
5412 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5413 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5414         : Drag (editor, atv->base_item ())
5415         , _ranges (r)
5416         , _y_origin (atv->y_position())
5417         , _nothing_to_drag (false)
5418 {
5419         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5420         setup (atv->lines ());
5421 }
5422
5423 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5424 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5425         : Drag (editor, rv->get_canvas_group ())
5426         , _ranges (r)
5427         , _y_origin (rv->get_time_axis_view().y_position())
5428         , _nothing_to_drag (false)
5429         , _integral (false)
5430 {
5431         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5432
5433         list<boost::shared_ptr<AutomationLine> > lines;
5434
5435         AudioRegionView*      audio_view;
5436         AutomationRegionView* automation_view;
5437         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5438                 lines.push_back (audio_view->get_gain_line ());
5439         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5440                 lines.push_back (automation_view->line ());
5441                 _integral = true;
5442         } else {
5443                 error << _("Automation range drag created for invalid region type") << endmsg;
5444         }
5445
5446         setup (lines);
5447 }
5448
5449 /** @param lines AutomationLines to drag.
5450  *  @param offset Offset from the session start to the points in the AutomationLines.
5451  */
5452 void
5453 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5454 {
5455         /* find the lines that overlap the ranges being dragged */
5456         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5457         while (i != lines.end ()) {
5458                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5459                 ++j;
5460
5461                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5462
5463                 /* check this range against all the AudioRanges that we are using */
5464                 list<AudioRange>::const_iterator k = _ranges.begin ();
5465                 while (k != _ranges.end()) {
5466                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5467                                 break;
5468                         }
5469                         ++k;
5470                 }
5471
5472                 /* add it to our list if it overlaps at all */
5473                 if (k != _ranges.end()) {
5474                         Line n;
5475                         n.line = *i;
5476                         n.state = 0;
5477                         n.range = r;
5478                         _lines.push_back (n);
5479                 }
5480
5481                 i = j;
5482         }
5483
5484         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5485 }
5486
5487 double
5488 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5489 {
5490         return 1.0 - ((global_y - _y_origin) / line->height());
5491 }
5492
5493 double
5494 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5495 {
5496         const double v = list->eval(x);
5497         return _integral ? rint(v) : v;
5498 }
5499
5500 void
5501 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5502 {
5503         Drag::start_grab (event, cursor);
5504
5505         /* Get line states before we start changing things */
5506         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5507                 i->state = &i->line->get_state ();
5508                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5509         }
5510
5511         if (_ranges.empty()) {
5512
5513                 /* No selected time ranges: drag all points */
5514                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5515                         uint32_t const N = i->line->npoints ();
5516                         for (uint32_t j = 0; j < N; ++j) {
5517                                 i->points.push_back (i->line->nth (j));
5518                         }
5519                 }
5520
5521         } else {
5522
5523                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5524
5525                         framecnt_t const half = (i->start + i->end) / 2;
5526
5527                         /* find the line that this audio range starts in */
5528                         list<Line>::iterator j = _lines.begin();
5529                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5530                                 ++j;
5531                         }
5532
5533                         if (j != _lines.end()) {
5534                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5535
5536                                 /* j is the line that this audio range starts in; fade into it;
5537                                    64 samples length plucked out of thin air.
5538                                 */
5539
5540                                 framepos_t a = i->start + 64;
5541                                 if (a > half) {
5542                                         a = half;
5543                                 }
5544
5545                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5546                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5547
5548                                 the_list->editor_add (p, value (the_list, p), false);
5549                                 the_list->editor_add (q, value (the_list, q), false);
5550                         }
5551
5552                         /* same thing for the end */
5553
5554                         j = _lines.begin();
5555                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5556                                 ++j;
5557                         }
5558
5559                         if (j != _lines.end()) {
5560                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5561
5562                                 /* j is the line that this audio range starts in; fade out of it;
5563                                    64 samples length plucked out of thin air.
5564                                 */
5565
5566                                 framepos_t b = i->end - 64;
5567                                 if (b < half) {
5568                                         b = half;
5569                                 }
5570
5571                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5572                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5573
5574                                 the_list->editor_add (p, value (the_list, p), false);
5575                                 the_list->editor_add (q, value (the_list, q), false);
5576                         }
5577                 }
5578
5579                 _nothing_to_drag = true;
5580
5581                 /* Find all the points that should be dragged and put them in the relevant
5582                    points lists in the Line structs.
5583                 */
5584
5585                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5586
5587                         uint32_t const N = i->line->npoints ();
5588                         for (uint32_t j = 0; j < N; ++j) {
5589
5590                                 /* here's a control point on this line */
5591                                 ControlPoint* p = i->line->nth (j);
5592                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5593
5594                                 /* see if it's inside a range */
5595                                 list<AudioRange>::const_iterator k = _ranges.begin ();
5596                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5597                                         ++k;
5598                                 }
5599
5600                                 if (k != _ranges.end()) {
5601                                         /* dragging this point */
5602                                         _nothing_to_drag = false;
5603                                         i->points.push_back (p);
5604                                 }
5605                         }
5606                 }
5607         }
5608
5609         if (_nothing_to_drag) {
5610                 return;
5611         }
5612 }
5613
5614 void
5615 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5616 {
5617         if (_nothing_to_drag) {
5618                 return;
5619         }
5620
5621         if (first_move) {
5622                 _editor->begin_reversible_command (_("automation range move"));
5623                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5624                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5625                 }
5626         }
5627
5628         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5629                 float const f = y_fraction (l->line, current_pointer_y());
5630                 /* we are ignoring x position for this drag, so we can just pass in anything */
5631                 uint32_t ignored;
5632                 l->line->drag_motion (0, f, true, false, ignored);
5633                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5634         }
5635 }
5636
5637 void
5638 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5639 {
5640         if (_nothing_to_drag || !motion_occurred) {
5641                 return;
5642         }
5643
5644         motion (event, false);
5645         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5646                 i->line->end_drag (false, 0);
5647         }
5648
5649         _editor->commit_reversible_command ();
5650 }
5651
5652 void
5653 AutomationRangeDrag::aborted (bool)
5654 {
5655         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5656                 i->line->reset ();
5657         }
5658 }
5659
5660 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5661         : view (v)
5662         , initial_time_axis_view (itav)
5663 {
5664         /* note that time_axis_view may be null if the regionview was created
5665          * as part of a copy operation.
5666          */
5667         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5668         layer = v->region()->layer ();
5669         initial_y = v->get_canvas_group()->position().y;
5670         initial_playlist = v->region()->playlist ();
5671         initial_position = v->region()->position ();
5672         initial_end = v->region()->position () + v->region()->length ();
5673 }
5674
5675 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5676         : Drag (e, i->canvas_item ())
5677         , _region_view (r)
5678         , _patch_change (i)
5679         , _cumulative_dx (0)
5680 {
5681         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5682                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5683                                                    grab_frame()));
5684 }
5685
5686 void
5687 PatchChangeDrag::motion (GdkEvent* ev, bool)
5688 {
5689         framepos_t f = adjusted_current_frame (ev);
5690         boost::shared_ptr<Region> r = _region_view->region ();
5691         f = max (f, r->position ());
5692         f = min (f, r->last_frame ());
5693
5694         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5695         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5696         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5697         _cumulative_dx = dxu;
5698 }
5699
5700 void
5701 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5702 {
5703         if (!movement_occurred) {
5704                 return;
5705         }
5706
5707         boost::shared_ptr<Region> r (_region_view->region ());
5708         framepos_t f = adjusted_current_frame (ev);
5709         f = max (f, r->position ());
5710         f = min (f, r->last_frame ());
5711
5712         _region_view->move_patch_change (
5713                 *_patch_change,
5714                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5715                 );
5716 }
5717
5718 void
5719 PatchChangeDrag::aborted (bool)
5720 {
5721         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5722 }
5723
5724 void
5725 PatchChangeDrag::setup_pointer_frame_offset ()
5726 {
5727         boost::shared_ptr<Region> region = _region_view->region ();
5728         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5729 }
5730
5731 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5732         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5733         , _region_view (rv)
5734 {
5735
5736 }
5737
5738 void
5739 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5740 {
5741         _region_view->update_drag_selection (
5742                 x1, x2, y1, y2,
5743                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5744 }
5745
5746 void
5747 MidiRubberbandSelectDrag::deselect_things ()
5748 {
5749         /* XXX */
5750 }
5751
5752 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5753         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5754         , _region_view (rv)
5755 {
5756         _vertical_only = true;
5757 }
5758
5759 void
5760 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5761 {
5762         double const y = _region_view->midi_view()->y_position ();
5763
5764         y1 = max (0.0, y1 - y);
5765         y2 = max (0.0, y2 - y);
5766
5767         _region_view->update_vertical_drag_selection (
5768                 y1,
5769                 y2,
5770                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5771                 );
5772 }
5773
5774 void
5775 MidiVerticalSelectDrag::deselect_things ()
5776 {
5777         /* XXX */
5778 }
5779
5780 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5781         : RubberbandSelectDrag (e, i)
5782 {
5783
5784 }
5785
5786 void
5787 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5788 {
5789         if (drag_in_progress) {
5790                 /* We just want to select things at the end of the drag, not during it */
5791                 return;
5792         }
5793
5794         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5795
5796         _editor->begin_reversible_selection_op (X_("rubberband selection"));
5797
5798         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5799
5800         _editor->commit_reversible_selection_op ();
5801 }
5802
5803 void
5804 EditorRubberbandSelectDrag::deselect_things ()
5805 {
5806         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5807
5808         _editor->selection->clear_tracks();
5809         _editor->selection->clear_regions();
5810         _editor->selection->clear_points ();
5811         _editor->selection->clear_lines ();
5812         _editor->selection->clear_midi_notes ();
5813
5814         _editor->commit_reversible_selection_op();
5815 }
5816
5817 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5818         : Drag (e, i)
5819         , _region_view (rv)
5820         , _drag_rect (0)
5821 {
5822         _note[0] = _note[1] = 0;
5823 }
5824
5825 NoteCreateDrag::~NoteCreateDrag ()
5826 {
5827         delete _drag_rect;
5828 }
5829
5830 framecnt_t
5831 NoteCreateDrag::grid_frames (framepos_t t) const
5832 {
5833         bool success;
5834         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5835         if (!success) {
5836                 grid_beats = Evoral::Beats(1);
5837         }
5838
5839         return _region_view->region_beats_to_region_frames (grid_beats);
5840 }
5841
5842 void
5843 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5844 {
5845         Drag::start_grab (event, cursor);
5846
5847         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5848
5849         framepos_t pf = _drags->current_pointer_frame ();
5850         framecnt_t const g = grid_frames (pf);
5851
5852         /* Hack so that we always snap to the note that we are over, instead of snapping
5853            to the next one if we're more than halfway through the one we're over.
5854         */
5855         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5856                 pf -= g / 2;
5857         }
5858
5859         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5860         _note[1] = _note[0];
5861
5862         MidiStreamView* sv = _region_view->midi_stream_view ();
5863         double const x = _editor->sample_to_pixel (_note[0]);
5864         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5865
5866         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5867         _drag_rect->set_outline_all ();
5868         _drag_rect->set_outline_color (0xffffff99);
5869         _drag_rect->set_fill_color (0xffffff66);
5870 }
5871
5872 void
5873 NoteCreateDrag::motion (GdkEvent* event, bool)
5874 {
5875         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5876         double const x0 = _editor->sample_to_pixel (_note[0]);
5877         double const x1 = _editor->sample_to_pixel (_note[1]);
5878         _drag_rect->set_x0 (std::min(x0, x1));
5879         _drag_rect->set_x1 (std::max(x0, x1));
5880 }
5881
5882 void
5883 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5884 {
5885         if (!had_movement) {
5886                 return;
5887         }
5888
5889         framepos_t const start = min (_note[0], _note[1]);
5890         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5891
5892         framecnt_t const g = grid_frames (start);
5893         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5894
5895         if (_editor->snap_mode() == SnapNormal && length < g) {
5896                 length = g;
5897         }
5898
5899         Evoral::Beats length_beats = max (
5900                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5901
5902         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5903 }
5904
5905 double
5906 NoteCreateDrag::y_to_region (double y) const
5907 {
5908         double x = 0;
5909         _region_view->get_canvas_group()->canvas_to_item (x, y);
5910         return y;
5911 }
5912
5913 void
5914 NoteCreateDrag::aborted (bool)
5915 {
5916
5917 }
5918
5919 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5920         : Drag (e, i)
5921         , arv (rv)
5922         , start (start_yn)
5923 {
5924         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5925 }
5926
5927 void
5928 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5929 {
5930         Drag::start_grab (event, cursor);
5931 }
5932
5933 void
5934 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5935 {
5936         double distance;
5937         double new_length;
5938         framecnt_t len;
5939
5940         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5941
5942         if (start) {
5943                 distance = _drags->current_pointer_x() - grab_x();
5944                 len = ar->fade_in()->back()->when;
5945         } else {
5946                 distance = grab_x() - _drags->current_pointer_x();
5947                 len = ar->fade_out()->back()->when;
5948         }
5949
5950         /* how long should it be ? */
5951
5952         new_length = len + _editor->pixel_to_sample (distance);
5953
5954         /* now check with the region that this is legal */
5955
5956         new_length = ar->verify_xfade_bounds (new_length, start);
5957
5958         if (start) {
5959                 arv->reset_fade_in_shape_width (ar, new_length);
5960         } else {
5961                 arv->reset_fade_out_shape_width (ar, new_length);
5962         }
5963 }
5964
5965 void
5966 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5967 {
5968         double distance;
5969         double new_length;
5970         framecnt_t len;
5971
5972         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5973
5974         if (start) {
5975                 distance = _drags->current_pointer_x() - grab_x();
5976                 len = ar->fade_in()->back()->when;
5977         } else {
5978                 distance = grab_x() - _drags->current_pointer_x();
5979                 len = ar->fade_out()->back()->when;
5980         }
5981
5982         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5983
5984         _editor->begin_reversible_command ("xfade trim");
5985         ar->playlist()->clear_owned_changes ();
5986
5987         if (start) {
5988                 ar->set_fade_in_length (new_length);
5989         } else {
5990                 ar->set_fade_out_length (new_length);
5991         }
5992
5993         /* Adjusting the xfade may affect other regions in the playlist, so we need
5994            to get undo Commands from the whole playlist rather than just the
5995            region.
5996         */
5997
5998         vector<Command*> cmds;
5999         ar->playlist()->rdiff (cmds);
6000         _editor->session()->add_commands (cmds);
6001         _editor->commit_reversible_command ();
6002
6003 }
6004
6005 void
6006 CrossfadeEdgeDrag::aborted (bool)
6007 {
6008         if (start) {
6009                 // arv->redraw_start_xfade ();
6010         } else {
6011                 // arv->redraw_end_xfade ();
6012         }
6013 }
6014
6015 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6016         : Drag (e, item, true)
6017         , line (new EditorCursor (*e))
6018 {
6019         line->set_position (pos);
6020         line->show ();
6021 }
6022
6023 RegionCutDrag::~RegionCutDrag ()
6024 {
6025         delete line;
6026 }
6027
6028 void
6029 RegionCutDrag::motion (GdkEvent*, bool)
6030 {
6031         framepos_t where = _drags->current_pointer_frame();
6032         _editor->snap_to (where);
6033
6034         line->set_position (where);
6035 }
6036
6037 void
6038 RegionCutDrag::finished (GdkEvent*, bool)
6039 {
6040         _editor->get_track_canvas()->canvas()->re_enter();
6041
6042         framepos_t pos = _drags->current_pointer_frame();
6043
6044         line->hide ();
6045
6046         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6047
6048         if (rs.empty()) {
6049                 return;
6050         }
6051
6052         _editor->split_regions_at (pos, rs);
6053 }
6054
6055 void
6056 RegionCutDrag::aborted (bool)
6057 {
6058 }