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