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