undoing a create note drag restores screen position.
[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         if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
3452                 double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3453                 stringstream strs;
3454                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), true);
3455                 strs << "end:" << fixed << setprecision(3) << new_bpm;
3456                 show_verbose_cursor_text (strs.str());
3457
3458         } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3459                 /* use vertical movement to alter tempo .. should be log */
3460                 double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3461                 stringstream strs;
3462                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()), false);
3463                 strs << "start:" << fixed << setprecision(3) << new_bpm;
3464                 show_verbose_cursor_text (strs.str());
3465
3466         } else if (_movable && !_real_section->locked_to_meter()) {
3467                 framepos_t pf;
3468
3469                 if (_editor->snap_musical()) {
3470                         /* we can't snap to a grid that we are about to move.
3471                          * gui_move_tempo() will sort out snap using the supplied beat divisions.
3472                         */
3473                         pf = adjusted_current_frame (event, false);
3474                 } else {
3475                         pf = adjusted_current_frame (event);
3476                 }
3477
3478                 /* snap to beat is 1, snap to bar is -1 (sorry) */
3479                 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3480
3481                 map.gui_set_tempo_position (_real_section, pf, sub_num);
3482
3483                 show_verbose_cursor_time (_real_section->frame());
3484         }
3485         _marker->set_position (adjusted_current_frame (event, false));
3486 }
3487
3488 void
3489 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3490 {
3491         if (!_real_section->active()) {
3492                 return;
3493         }
3494         if (!movement_occurred) {
3495                 if (was_double_click()) {
3496                         _editor->edit_tempo_marker (*_marker);
3497                 }
3498                 return;
3499         }
3500
3501         TempoMap& map (_editor->session()->tempo_map());
3502
3503         XMLNode &after = map.get_state();
3504         _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3505         _editor->commit_reversible_command ();
3506
3507         // delete the dummy marker we used for visual representation while moving.
3508         // a new visual marker will show up automatically.
3509         delete _marker;
3510 }
3511
3512 void
3513 TempoMarkerDrag::aborted (bool moved)
3514 {
3515         _marker->set_position (_marker->tempo().frame());
3516         if (moved) {
3517                 TempoMap& map (_editor->session()->tempo_map());
3518                 map.set_state (*before_state, Stateful::current_state_version);
3519                 // delete the dummy (hidden) marker we used for events while moving.
3520                 delete _marker;
3521         }
3522 }
3523
3524 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3525         : Drag (e, i)
3526         , _grab_qn (0.0)
3527         , _tempo (0)
3528         , before_state (0)
3529 {
3530         DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3531
3532 }
3533
3534 void
3535 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3536 {
3537         Drag::start_grab (event, cursor);
3538         TempoMap& map (_editor->session()->tempo_map());
3539         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3540         _editor->tempo_curve_selected (_tempo, true);
3541
3542         ostringstream sstr;
3543         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3544         show_verbose_cursor_text (sstr.str());
3545 }
3546
3547 void
3548 BBTRulerDrag::setup_pointer_frame_offset ()
3549 {
3550         TempoMap& map (_editor->session()->tempo_map());
3551         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3552         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3553         double beat = 0.0;
3554
3555         if (divisions > 0) {
3556                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3557         } else {
3558                 /* while it makes some sense for the user to determine the division to 'grab',
3559                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3560                    and the result over steep tempo curves. Use sixteenths.
3561                 */
3562                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3563         }
3564
3565         _grab_qn = map.quarter_note_at_beat (beat);
3566
3567         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3568
3569 }
3570
3571 void
3572 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3573 {
3574         TempoMap& map (_editor->session()->tempo_map());
3575
3576         if (first_move) {
3577                 /* get current state */
3578                 before_state = &map.get_state();
3579                 _editor->begin_reversible_command (_("stretch tempo"));
3580         }
3581
3582         framepos_t pf;
3583
3584         if (_editor->snap_musical()) {
3585                 pf = adjusted_current_frame (event, false);
3586         } else {
3587                 pf = adjusted_current_frame (event);
3588         }
3589
3590         if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3591                 /* adjust previous tempo to match pointer frame */
3592                 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3593         }
3594         ostringstream sstr;
3595         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
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         _editor->tempo_curve_selected (_tempo, false);
3612 }
3613
3614 void
3615 BBTRulerDrag::aborted (bool moved)
3616 {
3617         if (moved) {
3618                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3619         }
3620 }
3621
3622 TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
3623         : Drag (e, i)
3624         , _grab_qn (0.0)
3625         , _grab_tempo (0.0)
3626         , _tempo (0)
3627         , _next_tempo (0)
3628         , _drag_valid (true)
3629         , before_state (0)
3630 {
3631         DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
3632
3633 }
3634
3635 void
3636 TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3637 {
3638         Drag::start_grab (event, cursor);
3639         TempoMap& map (_editor->session()->tempo_map());
3640         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3641
3642         _next_tempo = map.next_tempo_section (_tempo);
3643         if (_next_tempo) {
3644                 if (!map.next_tempo_section (_next_tempo)) {
3645                         _drag_valid = false;
3646                         finished (event, false);
3647
3648                         return;
3649                 }
3650                 _editor->tempo_curve_selected (_tempo, true);
3651                 _editor->tempo_curve_selected (_next_tempo, true);
3652
3653                 ostringstream sstr;
3654                 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3655                 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3656                 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3657                 show_verbose_cursor_text (sstr.str());
3658         } else {
3659                 _drag_valid = false;
3660         }
3661
3662         _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
3663 }
3664
3665 void
3666 TempoTwistDrag::setup_pointer_frame_offset ()
3667 {
3668         TempoMap& map (_editor->session()->tempo_map());
3669         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3670         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3671         double beat = 0.0;
3672
3673         if (divisions > 0) {
3674                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3675         } else {
3676                 /* while it makes some sense for the user to determine the division to 'grab',
3677                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3678                    and the result over steep tempo curves. Use sixteenths.
3679                 */
3680                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3681         }
3682
3683         _grab_qn = map.quarter_note_at_beat (beat);
3684
3685         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3686
3687 }
3688
3689 void
3690 TempoTwistDrag::motion (GdkEvent* event, bool first_move)
3691 {
3692
3693         if (!_next_tempo || !_drag_valid) {
3694                 return;
3695         }
3696
3697         TempoMap& map (_editor->session()->tempo_map());
3698
3699         if (first_move) {
3700                 /* get current state */
3701                 before_state = &map.get_state();
3702                 _editor->begin_reversible_command (_("twist tempo"));
3703         }
3704
3705         framepos_t pf;
3706
3707         if (_editor->snap_musical()) {
3708                 pf = adjusted_current_frame (event, false);
3709         } else {
3710                 pf = adjusted_current_frame (event);
3711         }
3712
3713         /* adjust this and the next tempi to match pointer frame */
3714         double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3715         _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
3716
3717         ostringstream sstr;
3718         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3719         sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3720         sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3721         show_verbose_cursor_text (sstr.str());
3722 }
3723
3724 void
3725 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
3726 {
3727         TempoMap& map (_editor->session()->tempo_map());
3728
3729         if (!movement_occurred || !_drag_valid) {
3730                 return;
3731         }
3732
3733         _editor->tempo_curve_selected (_tempo, false);
3734         _editor->tempo_curve_selected (_next_tempo, false);
3735
3736         XMLNode &after = map.get_state();
3737         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3738         _editor->commit_reversible_command ();
3739 }
3740
3741 void
3742 TempoTwistDrag::aborted (bool moved)
3743 {
3744         if (moved) {
3745                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3746         }
3747 }
3748
3749 TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
3750         : Drag (e, i)
3751         , _grab_qn (0.0)
3752         , _tempo (0)
3753         , before_state (0)
3754 {
3755         DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
3756
3757 }
3758
3759 void
3760 TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3761 {
3762         Drag::start_grab (event, cursor);
3763         TempoMap& map (_editor->session()->tempo_map());
3764         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3765         _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), true);
3766
3767         ostringstream sstr;
3768         sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3769         show_verbose_cursor_text (sstr.str());
3770 }
3771
3772 void
3773 TempoEndDrag::setup_pointer_frame_offset ()
3774 {
3775         TempoMap& map (_editor->session()->tempo_map());
3776         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3777         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3778         double beat = 0.0;
3779
3780         if (divisions > 0) {
3781                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3782         } else {
3783                 /* while it makes some sense for the user to determine the division to 'grab',
3784                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3785                    and the result over steep tempo curves. Use sixteenths.
3786                 */
3787                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3788         }
3789
3790         _grab_qn = map.quarter_note_at_beat (beat);
3791
3792         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3793
3794 }
3795
3796 void
3797 TempoEndDrag::motion (GdkEvent* event, bool first_move)
3798 {
3799         TempoMap& map (_editor->session()->tempo_map());
3800
3801         if (first_move) {
3802                 /* get current state */
3803                 before_state = &map.get_state();
3804                 _editor->begin_reversible_command (_("stretch end tempo"));
3805         }
3806
3807
3808
3809         framepos_t const pf = adjusted_current_frame (event, false);
3810         map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
3811
3812         ostringstream sstr;
3813         sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute();
3814         show_verbose_cursor_text (sstr.str());
3815 }
3816
3817 void
3818 TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
3819 {
3820         if (!movement_occurred) {
3821                 return;
3822         }
3823
3824         TempoMap& map (_editor->session()->tempo_map());
3825
3826         XMLNode &after = map.get_state();
3827         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3828         _editor->commit_reversible_command ();
3829         _editor->tempo_curve_selected (&map.tempo_section_at_frame (_tempo->frame() - 1), false);
3830 }
3831
3832 void
3833 TempoEndDrag::aborted (bool moved)
3834 {
3835         if (moved) {
3836                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3837         }
3838 }
3839
3840 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3841         : Drag (e, &c.track_canvas_item(), false)
3842         , _cursor (c)
3843         , _stop (s)
3844         , _grab_zoom (0.0)
3845 {
3846         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3847 }
3848
3849 /** Do all the things we do when dragging the playhead to make it look as though
3850  *  we have located, without actually doing the locate (because that would cause
3851  *  the diskstream buffers to be refilled, which is too slow).
3852  */
3853 void
3854 CursorDrag::fake_locate (framepos_t t)
3855 {
3856         if (_editor->session () == 0) {
3857                 return;
3858         }
3859
3860         _editor->playhead_cursor->set_position (t);
3861
3862         Session* s = _editor->session ();
3863         if (s->timecode_transmission_suspended ()) {
3864                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3865                 /* This is asynchronous so it will be sent "now"
3866                  */
3867                 s->send_mmc_locate (f);
3868                 /* These are synchronous and will be sent during the next
3869                    process cycle
3870                 */
3871                 s->queue_full_time_code ();
3872                 s->queue_song_position_pointer ();
3873         }
3874
3875         show_verbose_cursor_time (t);
3876         _editor->UpdateAllTransportClocks (t);
3877 }
3878
3879 void
3880 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3881 {
3882         Drag::start_grab (event, c);
3883         setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
3884
3885         _grab_zoom = _editor->samples_per_pixel;
3886
3887         MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3888
3889         _editor->snap_to_with_modifier (where, event);
3890         _editor->_dragging_playhead = true;
3891         _editor->_control_scroll_target = where.frame;
3892
3893         Session* s = _editor->session ();
3894
3895         /* grab the track canvas item as well */
3896
3897         _cursor.track_canvas_item().grab();
3898
3899         if (s) {
3900                 if (_was_rolling && _stop) {
3901                         s->request_stop ();
3902                 }
3903
3904                 if (s->is_auditioning()) {
3905                         s->cancel_audition ();
3906                 }
3907
3908
3909                 if (AudioEngine::instance()->connected()) {
3910
3911                         /* do this only if we're the engine is connected
3912                          * because otherwise this request will never be
3913                          * serviced and we'll busy wait forever. likewise,
3914                          * notice if we are disconnected while waiting for the
3915                          * request to be serviced.
3916                          */
3917
3918                         s->request_suspend_timecode_transmission ();
3919                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3920                                 /* twiddle our thumbs */
3921                         }
3922                 }
3923         }
3924
3925         fake_locate (where.frame - snap_delta (event->button.state));
3926 }
3927
3928 void
3929 CursorDrag::motion (GdkEvent* event, bool)
3930 {
3931         MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3932
3933         _editor->snap_to_with_modifier (where, event);
3934
3935         if (where.frame != last_pointer_frame()) {
3936                 fake_locate (where.frame - snap_delta (event->button.state));
3937         }
3938 }
3939
3940 void
3941 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3942 {
3943         _editor->_dragging_playhead = false;
3944
3945         _cursor.track_canvas_item().ungrab();
3946
3947         if (!movement_occurred && _stop) {
3948                 return;
3949         }
3950
3951         motion (event, false);
3952
3953         Session* s = _editor->session ();
3954         if (s) {
3955                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3956                 _editor->_pending_locate_request = true;
3957                 s->request_resume_timecode_transmission ();
3958         }
3959 }
3960
3961 void
3962 CursorDrag::aborted (bool)
3963 {
3964         _cursor.track_canvas_item().ungrab();
3965
3966         if (_editor->_dragging_playhead) {
3967                 _editor->session()->request_resume_timecode_transmission ();
3968                 _editor->_dragging_playhead = false;
3969         }
3970
3971         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
3972 }
3973
3974 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3975         : RegionDrag (e, i, p, v)
3976 {
3977         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3978 }
3979
3980 void
3981 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3982 {
3983         Drag::start_grab (event, cursor);
3984
3985         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3986         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3987         setup_snap_delta (MusicFrame (r->position(), 0));
3988
3989         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3990 }
3991
3992 void
3993 FadeInDrag::setup_pointer_frame_offset ()
3994 {
3995         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3996         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3997         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3998 }
3999
4000 void
4001 FadeInDrag::motion (GdkEvent* event, bool)
4002 {
4003         framecnt_t fade_length;
4004
4005         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4006         _editor->snap_to_with_modifier (pos, event);
4007
4008         pos.frame -= snap_delta (event->button.state);
4009
4010         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4011
4012         if (pos.frame < (region->position() + 64)) {
4013                 fade_length = 64; // this should be a minimum defined somewhere
4014         } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
4015                 fade_length = region->length() - region->fade_out()->back()->when - 1;
4016         } else {
4017                 fade_length = pos.frame - region->position();
4018         }
4019
4020         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4021
4022                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4023
4024                 if (!tmp) {
4025                         continue;
4026                 }
4027
4028                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
4029         }
4030
4031         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
4032 }
4033
4034 void
4035 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
4036 {
4037         if (!movement_occurred) {
4038                 return;
4039         }
4040
4041         framecnt_t fade_length;
4042         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4043
4044         _editor->snap_to_with_modifier (pos, event);
4045         pos.frame -= snap_delta (event->button.state);
4046
4047         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4048
4049         if (pos.frame < (region->position() + 64)) {
4050                 fade_length = 64; // this should be a minimum defined somewhere
4051         } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
4052                 fade_length = region->length() - region->fade_out()->back()->when - 1;
4053         } else {
4054                 fade_length = pos.frame - region->position();
4055         }
4056
4057         bool in_command = false;
4058
4059         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4060
4061                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4062
4063                 if (!tmp) {
4064                         continue;
4065                 }
4066
4067                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4068                 XMLNode &before = alist->get_state();
4069
4070                 tmp->audio_region()->set_fade_in_length (fade_length);
4071                 tmp->audio_region()->set_fade_in_active (true);
4072
4073                 if (!in_command) {
4074                         _editor->begin_reversible_command (_("change fade in length"));
4075                         in_command = true;
4076                 }
4077                 XMLNode &after = alist->get_state();
4078                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4079         }
4080
4081         if (in_command) {
4082                 _editor->commit_reversible_command ();
4083         }
4084 }
4085
4086 void
4087 FadeInDrag::aborted (bool)
4088 {
4089         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4090                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4091
4092                 if (!tmp) {
4093                         continue;
4094                 }
4095
4096                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
4097         }
4098 }
4099
4100 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4101         : RegionDrag (e, i, p, v)
4102 {
4103         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
4104 }
4105
4106 void
4107 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4108 {
4109         Drag::start_grab (event, cursor);
4110
4111         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4112         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4113         setup_snap_delta (MusicFrame (r->last_frame(), 0));
4114
4115         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
4116 }
4117
4118 void
4119 FadeOutDrag::setup_pointer_frame_offset ()
4120 {
4121         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4122         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4123         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
4124 }
4125
4126 void
4127 FadeOutDrag::motion (GdkEvent* event, bool)
4128 {
4129         framecnt_t fade_length;
4130         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4131
4132         _editor->snap_to_with_modifier (pos, event);
4133         pos.frame -= snap_delta (event->button.state);
4134
4135         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4136
4137         if (pos.frame > (region->last_frame() - 64)) {
4138                 fade_length = 64; // this should really be a minimum fade defined somewhere
4139         } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4140                 fade_length = region->length() - region->fade_in()->back()->when - 1;
4141         } else {
4142                 fade_length = region->last_frame() - pos.frame;
4143         }
4144
4145         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4146
4147                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4148
4149                 if (!tmp) {
4150                         continue;
4151                 }
4152
4153                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
4154         }
4155
4156         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
4157 }
4158
4159 void
4160 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
4161 {
4162         if (!movement_occurred) {
4163                 return;
4164         }
4165
4166         framecnt_t fade_length;
4167         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4168
4169         _editor->snap_to_with_modifier (pos, event);
4170         pos.frame -= snap_delta (event->button.state);
4171
4172         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4173
4174         if (pos.frame > (region->last_frame() - 64)) {
4175                 fade_length = 64; // this should really be a minimum fade defined somewhere
4176         } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4177                 fade_length = region->length() - region->fade_in()->back()->when - 1;
4178         } else {
4179                 fade_length = region->last_frame() - pos.frame;
4180         }
4181
4182         bool in_command = false;
4183
4184         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4185
4186                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4187
4188                 if (!tmp) {
4189                         continue;
4190                 }
4191
4192                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4193                 XMLNode &before = alist->get_state();
4194
4195                 tmp->audio_region()->set_fade_out_length (fade_length);
4196                 tmp->audio_region()->set_fade_out_active (true);
4197
4198                 if (!in_command) {
4199                         _editor->begin_reversible_command (_("change fade out length"));
4200                         in_command = true;
4201                 }
4202                 XMLNode &after = alist->get_state();
4203                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4204         }
4205
4206         if (in_command) {
4207                 _editor->commit_reversible_command ();
4208         }
4209 }
4210
4211 void
4212 FadeOutDrag::aborted (bool)
4213 {
4214         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4215                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4216
4217                 if (!tmp) {
4218                         continue;
4219                 }
4220
4221                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4222         }
4223 }
4224
4225 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4226         : Drag (e, i)
4227         , _selection_changed (false)
4228 {
4229         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4230         Gtk::Window* toplevel = _editor->current_toplevel();
4231         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4232
4233         assert (_marker);
4234
4235         _points.push_back (ArdourCanvas::Duple (0, 0));
4236
4237         _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4238 }
4239
4240 MarkerDrag::~MarkerDrag ()
4241 {
4242         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4243                 delete i->location;
4244         }
4245 }
4246
4247 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4248 {
4249         location = new Location (*l);
4250         markers.push_back (m);
4251         move_both = false;
4252 }
4253
4254 void
4255 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4256 {
4257         Drag::start_grab (event, cursor);
4258
4259         bool is_start;
4260
4261         Location *location = _editor->find_location_from_marker (_marker, is_start);
4262         _editor->_dragging_edit_point = true;
4263
4264         update_item (location);
4265
4266         // _drag_line->show();
4267         // _line->raise_to_top();
4268
4269         if (is_start) {
4270                 show_verbose_cursor_time (location->start());
4271         } else {
4272                 show_verbose_cursor_time (location->end());
4273         }
4274         setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
4275
4276         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4277
4278         switch (op) {
4279         case Selection::Toggle:
4280                 /* we toggle on the button release */
4281                 break;
4282         case Selection::Set:
4283                 if (!_editor->selection->selected (_marker)) {
4284                         _editor->selection->set (_marker);
4285                         _selection_changed = true;
4286                 }
4287                 break;
4288         case Selection::Extend:
4289         {
4290                 Locations::LocationList ll;
4291                 list<ArdourMarker*> to_add;
4292                 framepos_t s, e;
4293                 _editor->selection->markers.range (s, e);
4294                 s = min (_marker->position(), s);
4295                 e = max (_marker->position(), e);
4296                 s = min (s, e);
4297                 e = max (s, e);
4298                 if (e < max_framepos) {
4299                         ++e;
4300                 }
4301                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4302                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4303                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4304                         if (lm) {
4305                                 if (lm->start) {
4306                                         to_add.push_back (lm->start);
4307                                 }
4308                                 if (lm->end) {
4309                                         to_add.push_back (lm->end);
4310                                 }
4311                         }
4312                 }
4313                 if (!to_add.empty()) {
4314                         _editor->selection->add (to_add);
4315                         _selection_changed = true;
4316                 }
4317                 break;
4318         }
4319         case Selection::Add:
4320                 _editor->selection->add (_marker);
4321                 _selection_changed = true;
4322
4323                 break;
4324         }
4325
4326         /* Set up copies for us to manipulate during the drag
4327          */
4328
4329         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4330
4331                 Location* l = _editor->find_location_from_marker (*i, is_start);
4332
4333                 if (!l) {
4334                         continue;
4335                 }
4336
4337                 if (l->is_mark()) {
4338                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4339                 } else {
4340                         /* range: check that the other end of the range isn't
4341                            already there.
4342                         */
4343                         CopiedLocationInfo::iterator x;
4344                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4345                                 if (*(*x).location == *l) {
4346                                         break;
4347                                 }
4348                         }
4349                         if (x == _copied_locations.end()) {
4350                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4351                         } else {
4352                                 (*x).markers.push_back (*i);
4353                                 (*x).move_both = true;
4354                         }
4355                 }
4356
4357         }
4358 }
4359
4360 void
4361 MarkerDrag::setup_pointer_frame_offset ()
4362 {
4363         bool is_start;
4364         Location *location = _editor->find_location_from_marker (_marker, is_start);
4365         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4366 }
4367
4368 void
4369 MarkerDrag::motion (GdkEvent* event, bool)
4370 {
4371         framecnt_t f_delta = 0;
4372         bool is_start;
4373         bool move_both = false;
4374         Location *real_location;
4375         Location *copy_location = 0;
4376         framecnt_t const sd = snap_delta (event->button.state);
4377
4378         framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4379         framepos_t next = newframe;
4380
4381         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4382                 move_both = true;
4383         }
4384
4385         CopiedLocationInfo::iterator x;
4386
4387         /* find the marker we're dragging, and compute the delta */
4388
4389         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4390
4391                 copy_location = (*x).location;
4392
4393                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4394
4395                         /* this marker is represented by this
4396                          * CopiedLocationMarkerInfo
4397                          */
4398
4399                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4400                                 /* que pasa ?? */
4401                                 return;
4402                         }
4403
4404                         if (real_location->is_mark()) {
4405                                 f_delta = newframe - copy_location->start();
4406                         } else {
4407
4408
4409                                 switch (_marker->type()) {
4410                                 case ArdourMarker::SessionStart:
4411                                 case ArdourMarker::RangeStart:
4412                                 case ArdourMarker::LoopStart:
4413                                 case ArdourMarker::PunchIn:
4414                                         f_delta = newframe - copy_location->start();
4415                                         break;
4416
4417                                 case ArdourMarker::SessionEnd:
4418                                 case ArdourMarker::RangeEnd:
4419                                 case ArdourMarker::LoopEnd:
4420                                 case ArdourMarker::PunchOut:
4421                                         f_delta = newframe - copy_location->end();
4422                                         break;
4423                                 default:
4424                                         /* what kind of marker is this ? */
4425                                         return;
4426                                 }
4427                         }
4428
4429                         break;
4430                 }
4431         }
4432
4433         if (x == _copied_locations.end()) {
4434                 /* hmm, impossible - we didn't find the dragged marker */
4435                 return;
4436         }
4437
4438         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4439
4440         /* now move them all */
4441
4442         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4443
4444                 copy_location = x->location;
4445
4446                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4447                         continue;
4448                 }
4449
4450                 if (real_location->locked()) {
4451                         continue;
4452                 }
4453
4454                 if (copy_location->is_mark()) {
4455
4456                         /* now move it */
4457                         copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4458
4459                 } else {
4460
4461                         framepos_t new_start = copy_location->start() + f_delta;
4462                         framepos_t new_end = copy_location->end() + f_delta;
4463
4464                         if (is_start) { // start-of-range marker
4465
4466                                 if (move_both || (*x).move_both) {
4467                                         copy_location->set_start (new_start, false, true, divisions);
4468                                         copy_location->set_end (new_end, false, true, divisions);
4469                                 } else  if (new_start < copy_location->end()) {
4470                                         copy_location->set_start (new_start, false, true, divisions);
4471                                 } else if (newframe > 0) {
4472                                         //_editor->snap_to (next, RoundUpAlways, true);
4473                                         copy_location->set_end (next, false, true, divisions);
4474                                         copy_location->set_start (newframe, false, true, divisions);
4475                                 }
4476
4477                         } else { // end marker
4478
4479                                 if (move_both || (*x).move_both) {
4480                                         copy_location->set_end (new_end, divisions);
4481                                         copy_location->set_start (new_start, false, true, divisions);
4482                                 } else if (new_end > copy_location->start()) {
4483                                         copy_location->set_end (new_end, false, true, divisions);
4484                                 } else if (newframe > 0) {
4485                                         //_editor->snap_to (next, RoundDownAlways, true);
4486                                         copy_location->set_start (next, false, true, divisions);
4487                                         copy_location->set_end (newframe, false, true, divisions);
4488                                 }
4489                         }
4490                 }
4491
4492                 update_item (copy_location);
4493
4494                 /* now lookup the actual GUI items used to display this
4495                  * location and move them to wherever the copy of the location
4496                  * is now. This means that the logic in ARDOUR::Location is
4497                  * still enforced, even though we are not (yet) modifying
4498                  * the real Location itself.
4499                  */
4500
4501                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4502
4503                 if (lm) {
4504                         lm->set_position (copy_location->start(), copy_location->end());
4505                 }
4506
4507         }
4508
4509         assert (!_copied_locations.empty());
4510
4511         show_verbose_cursor_time (newframe);
4512 }
4513
4514 void
4515 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4516 {
4517         if (!movement_occurred) {
4518
4519                 if (was_double_click()) {
4520                         _editor->rename_marker (_marker);
4521                         return;
4522                 }
4523
4524                 /* just a click, do nothing but finish
4525                    off the selection process
4526                 */
4527
4528                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4529                 switch (op) {
4530                 case Selection::Set:
4531                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4532                                 _editor->selection->set (_marker);
4533                                 _selection_changed = true;
4534                         }
4535                         break;
4536
4537                 case Selection::Toggle:
4538                         /* we toggle on the button release, click only */
4539                         _editor->selection->toggle (_marker);
4540                         _selection_changed = true;
4541
4542                         break;
4543
4544                 case Selection::Extend:
4545                 case Selection::Add:
4546                         break;
4547                 }
4548
4549                 if (_selection_changed) {
4550                         _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4551                         _editor->commit_reversible_selection_op();
4552                 }
4553
4554                 return;
4555         }
4556
4557         _editor->_dragging_edit_point = false;
4558
4559         XMLNode &before = _editor->session()->locations()->get_state();
4560         bool in_command = false;
4561
4562         MarkerSelection::iterator i;
4563         CopiedLocationInfo::iterator x;
4564         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4565         bool is_start;
4566
4567         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4568              x != _copied_locations.end() && i != _editor->selection->markers.end();
4569              ++i, ++x) {
4570
4571                 Location * location = _editor->find_location_from_marker (*i, is_start);
4572
4573                 if (location) {
4574
4575                         if (location->locked()) {
4576                                 continue;
4577                         }
4578                         if (!in_command) {
4579                                 _editor->begin_reversible_command ( _("move marker") );
4580                                 in_command = true;
4581                         }
4582                         if (location->is_mark()) {
4583                                 location->set_start (((*x).location)->start(), false, true, divisions);
4584                         } else {
4585                                 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4586                         }
4587
4588                         if (location->is_session_range()) {
4589                                 _editor->session()->set_end_is_free (false);
4590                         }
4591                 }
4592         }
4593
4594         if (in_command) {
4595                 XMLNode &after = _editor->session()->locations()->get_state();
4596                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4597                 _editor->commit_reversible_command ();
4598         }
4599 }
4600
4601 void
4602 MarkerDrag::aborted (bool movement_occurred)
4603 {
4604         if (!movement_occurred) {
4605                 return;
4606         }
4607
4608         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4609
4610                 /* move all markers to their original location */
4611
4612
4613                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4614
4615                         bool is_start;
4616                         Location * location = _editor->find_location_from_marker (*m, is_start);
4617
4618                         if (location) {
4619                                 (*m)->set_position (is_start ? location->start() : location->end());
4620                         }
4621                 }
4622         }
4623 }
4624
4625 void
4626 MarkerDrag::update_item (Location*)
4627 {
4628         /* noop */
4629 }
4630
4631 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4632         : Drag (e, i)
4633         , _fixed_grab_x (0.0)
4634         , _fixed_grab_y (0.0)
4635         , _cumulative_x_drag (0.0)
4636         , _cumulative_y_drag (0.0)
4637         , _pushing (false)
4638         , _final_index (0)
4639 {
4640         if (_zero_gain_fraction < 0.0) {
4641                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4642         }
4643
4644         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4645
4646         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4647         assert (_point);
4648 }
4649
4650
4651 void
4652 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4653 {
4654         Drag::start_grab (event, _editor->cursors()->fader);
4655
4656         // start the grab at the center of the control point so
4657         // the point doesn't 'jump' to the mouse after the first drag
4658         _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4659         _fixed_grab_y = _point->get_y();
4660
4661         setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
4662
4663         float const fraction = 1 - (_point->get_y() / _point->line().height());
4664         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4665
4666         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4667
4668         if (!_point->can_slide ()) {
4669                 _x_constrained = true;
4670         }
4671 }
4672
4673 void
4674 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4675 {
4676         double dx = _drags->current_pointer_x() - last_pointer_x();
4677         double dy = current_pointer_y() - last_pointer_y();
4678         bool need_snap = true;
4679
4680         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4681                 dx *= 0.1;
4682                 dy *= 0.1;
4683                 need_snap = false;
4684         }
4685
4686         /* coordinate in pixels relative to the start of the region (for region-based automation)
4687            or track (for track-based automation) */
4688         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4689         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4690
4691         // calculate zero crossing point. back off by .01 to stay on the
4692         // positive side of zero
4693         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4694
4695         if (_x_constrained) {
4696                 cx = _fixed_grab_x;
4697         }
4698         if (_y_constrained) {
4699                 cy = _fixed_grab_y;
4700         }
4701
4702         _cumulative_x_drag = cx - _fixed_grab_x;
4703         _cumulative_y_drag = cy - _fixed_grab_y;
4704
4705         cx = max (0.0, cx);
4706         cy = max (0.0, cy);
4707         cy = min ((double) _point->line().height(), cy);
4708
4709         // make sure we hit zero when passing through
4710         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4711                 cy = zero_gain_y;
4712         }
4713
4714         MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4715
4716         if (!_x_constrained && need_snap) {
4717                 _editor->snap_to_with_modifier (cx_mf, event);
4718         }
4719
4720         cx_mf.frame -= snap_delta (event->button.state);
4721         cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4722
4723         float const fraction = 1.0 - (cy / _point->line().height());
4724
4725         if (first_motion) {
4726                 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4727                 _editor->begin_reversible_command (_("automation event move"));
4728                 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4729         }
4730         pair<double, float> result;
4731         result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4732
4733         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4734 }
4735
4736 void
4737 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4738 {
4739         if (!movement_occurred) {
4740
4741                 /* just a click */
4742                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4743                         _editor->reset_point_selection ();
4744                 }
4745
4746         } else {
4747                 _point->line().end_drag (_pushing, _final_index);
4748                 _editor->commit_reversible_command ();
4749         }
4750 }
4751
4752 void
4753 ControlPointDrag::aborted (bool)
4754 {
4755         _point->line().reset ();
4756 }
4757
4758 bool
4759 ControlPointDrag::active (Editing::MouseMode m)
4760 {
4761         if (m == Editing::MouseDraw) {
4762                 /* always active in mouse draw */
4763                 return true;
4764         }
4765
4766         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4767         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4768 }
4769
4770 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4771         : Drag (e, i)
4772         , _line (0)
4773         , _fixed_grab_x (0.0)
4774         , _fixed_grab_y (0.0)
4775         , _cumulative_y_drag (0)
4776         , _before (0)
4777         , _after (0)
4778 {
4779         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4780 }
4781
4782 void
4783 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4784 {
4785         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4786         assert (_line);
4787
4788         _item = &_line->grab_item ();
4789
4790         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4791            origin, and ditto for y.
4792         */
4793
4794         double mx = event->button.x;
4795         double my = event->button.y;
4796
4797         _line->grab_item().canvas_to_item (mx, my);
4798
4799         framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4800
4801         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4802                 /* no adjacent points */
4803                 return;
4804         }
4805
4806         Drag::start_grab (event, _editor->cursors()->fader);
4807
4808         /* store grab start in item frame */
4809         double const bx = _line->nth (_before)->get_x();
4810         double const ax = _line->nth (_after)->get_x();
4811         double const click_ratio = (ax - mx) / (ax - bx);
4812
4813         double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4814
4815         _fixed_grab_x = mx;
4816         _fixed_grab_y = cy;
4817
4818         double fraction = 1.0 - (cy / _line->height());
4819
4820         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4821 }
4822
4823 void
4824 LineDrag::motion (GdkEvent* event, bool first_move)
4825 {
4826         double dy = current_pointer_y() - last_pointer_y();
4827
4828         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4829                 dy *= 0.1;
4830         }
4831
4832         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4833
4834         _cumulative_y_drag = cy - _fixed_grab_y;
4835
4836         cy = max (0.0, cy);
4837         cy = min ((double) _line->height(), cy);
4838
4839         double const fraction = 1.0 - (cy / _line->height());
4840         uint32_t ignored;
4841
4842         if (first_move) {
4843                 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4844
4845                 _editor->begin_reversible_command (_("automation range move"));
4846                 _line->start_drag_line (_before, _after, initial_fraction);
4847         }
4848
4849         /* we are ignoring x position for this drag, so we can just pass in anything */
4850         pair<double, float> result;
4851
4852         result = _line->drag_motion (0, fraction, true, false, ignored);
4853         show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4854 }
4855
4856 void
4857 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4858 {
4859         if (movement_occurred) {
4860                 motion (event, false);
4861                 _line->end_drag (false, 0);
4862                 _editor->commit_reversible_command ();
4863         } else {
4864                 /* add a new control point on the line */
4865
4866                 AutomationTimeAxisView* atv;
4867
4868                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4869                         framepos_t where = grab_frame ();
4870
4871                         double cx = 0;
4872                         double cy = _fixed_grab_y;
4873
4874                         _line->grab_item().item_to_canvas (cx, cy);
4875
4876                         atv->add_automation_event (event, where, cy, false);
4877                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4878                         AudioRegionView* arv;
4879
4880                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4881                                 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4882                         }
4883                 }
4884         }
4885 }
4886
4887 void
4888 LineDrag::aborted (bool)
4889 {
4890         _line->reset ();
4891 }
4892
4893 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4894         : Drag (e, i),
4895           _line (0),
4896           _arv (0),
4897           _region_view_grab_x (0.0),
4898           _cumulative_x_drag (0),
4899           _before (0.0),
4900           _max_x (0)
4901 {
4902         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4903 }
4904
4905 void
4906 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4907 {
4908         Drag::start_grab (event);
4909
4910         _line = reinterpret_cast<Line*> (_item);
4911         assert (_line);
4912
4913         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4914
4915         double cx = event->button.x;
4916         double cy = event->button.y;
4917
4918         _item->parent()->canvas_to_item (cx, cy);
4919
4920         /* store grab start in parent frame */
4921         _region_view_grab_x = cx;
4922
4923         _before = *(float*) _item->get_data ("position");
4924
4925         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4926
4927         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4928 }
4929
4930 void
4931 FeatureLineDrag::motion (GdkEvent*, bool)
4932 {
4933         double dx = _drags->current_pointer_x() - last_pointer_x();
4934
4935         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4936
4937         _cumulative_x_drag += dx;
4938
4939         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4940
4941         if (cx > _max_x){
4942                 cx = _max_x;
4943         }
4944         else if(cx < 0){
4945                 cx = 0;
4946         }
4947
4948         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4949         assert (bbox);
4950         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4951
4952         float *pos = new float;
4953         *pos = cx;
4954
4955         _line->set_data ("position", pos);
4956
4957         _before = cx;
4958 }
4959
4960 void
4961 FeatureLineDrag::finished (GdkEvent*, bool)
4962 {
4963         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4964         _arv->update_transient(_before, _before);
4965 }
4966
4967 void
4968 FeatureLineDrag::aborted (bool)
4969 {
4970         //_line->reset ();
4971 }
4972
4973 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4974         : Drag (e, i)
4975         , _vertical_only (false)
4976 {
4977         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4978 }
4979
4980 void
4981 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4982 {
4983         Drag::start_grab (event);
4984         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4985 }
4986
4987 void
4988 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4989 {
4990         framepos_t start;
4991         framepos_t end;
4992         double y1;
4993         double y2;
4994         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4995         MusicFrame grab (grab_frame (), 0);
4996
4997         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4998                 _editor->snap_to_with_modifier (grab, event);
4999         } else {
5000                 grab.frame = raw_grab_frame ();
5001         }
5002
5003         /* base start and end on initial click position */
5004
5005         if (pf < grab.frame) {
5006                 start = pf;
5007                 end = grab.frame;
5008         } else {
5009                 end = pf;
5010                 start = grab.frame;
5011         }
5012
5013         if (current_pointer_y() < grab_y()) {
5014                 y1 = current_pointer_y();
5015                 y2 = grab_y();
5016         } else {
5017                 y2 = current_pointer_y();
5018                 y1 = grab_y();
5019         }
5020
5021         if (start != end || y1 != y2) {
5022
5023                 double x1 = _editor->sample_to_pixel (start);
5024                 double x2 = _editor->sample_to_pixel (end);
5025                 const double min_dimension = 2.0;
5026
5027                 if (_vertical_only) {
5028                         /* fixed 10 pixel width */
5029                         x2 = x1 + 10;
5030                 } else {
5031                         if (x2 < x1) {
5032                                 x2 = min (x1 - min_dimension, x2);
5033                         } else {
5034                                 x2 = max (x1 + min_dimension, x2);
5035                         }
5036                 }
5037
5038                 if (y2 < y1) {
5039                         y2 = min (y1 - min_dimension, y2);
5040                 } else {
5041                         y2 = max (y1 + min_dimension, y2);
5042                 }
5043
5044                 /* translate rect into item space and set */
5045
5046                 ArdourCanvas::Rect r (x1, y1, x2, y2);
5047
5048                 /* this drag is a _trackview_only == true drag, so the y1 and
5049                  * y2 (computed using current_pointer_y() and grab_y()) will be
5050                  * relative to the top of the trackview group). The
5051                  * rubberband rect has the same parent/scroll offset as the
5052                  * the trackview group, so we can use the "r" rect directly
5053                  * to set the shape of the rubberband.
5054                  */
5055
5056                 _editor->rubberband_rect->set (r);
5057                 _editor->rubberband_rect->show();
5058                 _editor->rubberband_rect->raise_to_top();
5059
5060                 show_verbose_cursor_time (pf);
5061
5062                 do_select_things (event, true);
5063         }
5064 }
5065
5066 void
5067 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
5068 {
5069         framepos_t x1;
5070         framepos_t x2;
5071         framepos_t grab = grab_frame ();
5072         framepos_t lpf = last_pointer_frame ();
5073
5074         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5075                 grab = raw_grab_frame ();
5076                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
5077         }
5078
5079         if (grab < lpf) {
5080                 x1 = grab;
5081                 x2 = lpf;
5082         } else {
5083                 x2 = grab;
5084                 x1 = lpf;
5085         }
5086
5087         double y1;
5088         double y2;
5089
5090         if (current_pointer_y() < grab_y()) {
5091                 y1 = current_pointer_y();
5092                 y2 = grab_y();
5093         } else {
5094                 y2 = current_pointer_y();
5095                 y1 = grab_y();
5096         }
5097
5098         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
5099 }
5100
5101 void
5102 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
5103 {
5104         if (movement_occurred) {
5105
5106                 motion (event, false);
5107                 do_select_things (event, false);
5108
5109         } else {
5110
5111                 /* just a click */
5112
5113                 bool do_deselect = true;
5114                 MidiTimeAxisView* mtv;
5115
5116                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
5117                         /* MIDI track */
5118                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
5119                                 /* nothing selected */
5120                                 add_midi_region (mtv, true);
5121                                 do_deselect = false;
5122                         }
5123                 }
5124
5125                 /* do not deselect if Primary or Tertiary (toggle-select or
5126                  * extend-select are pressed.
5127                  */
5128
5129                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
5130                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
5131                     do_deselect) {
5132                         deselect_things ();
5133                 }
5134
5135         }
5136
5137         _editor->rubberband_rect->hide();
5138 }
5139
5140 void
5141 RubberbandSelectDrag::aborted (bool)
5142 {
5143         _editor->rubberband_rect->hide ();
5144 }
5145
5146 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
5147         : RegionDrag (e, i, p, v)
5148 {
5149         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
5150 }
5151
5152 void
5153 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5154 {
5155         Drag::start_grab (event, cursor);
5156
5157         _editor->get_selection().add (_primary);
5158
5159         MusicFrame where (_primary->region()->position(), 0);
5160         setup_snap_delta (where);
5161
5162         show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
5163 }
5164
5165 void
5166 TimeFXDrag::motion (GdkEvent* event, bool)
5167 {
5168         RegionView* rv = _primary;
5169         StreamView* cv = rv->get_time_axis_view().view ();
5170         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
5171         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
5172         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
5173         MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
5174
5175         _editor->snap_to_with_modifier (pf, event);
5176         pf.frame -= snap_delta (event->button.state);
5177
5178         if (pf.frame > rv->region()->position()) {
5179                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
5180         }
5181
5182         show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
5183 }
5184
5185 void
5186 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
5187 {
5188         /* this may have been a single click, no drag. We still want the dialog
5189            to show up in that case, so that the user can manually edit the
5190            parameters for the timestretch.
5191         */
5192
5193         float fraction = 1.0;
5194
5195         if (movement_occurred) {
5196
5197                 motion (event, false);
5198
5199                 _primary->get_time_axis_view().hide_timestretch ();
5200
5201                 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
5202
5203                 if (adjusted_frame_pos < _primary->region()->position()) {
5204                         /* backwards drag of the left edge - not usable */
5205                         return;
5206                 }
5207
5208                 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
5209
5210                 fraction = (double) newlen / (double) _primary->region()->length();
5211
5212 #ifndef USE_RUBBERBAND
5213                 // Soundtouch uses fraction / 100 instead of normal (/ 1)
5214                 if (_primary->region()->data_type() == DataType::AUDIO) {
5215                         fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
5216                 }
5217 #endif
5218         }
5219
5220         if (!_editor->get_selection().regions.empty()) {
5221                 /* primary will already be included in the selection, and edit
5222                    group shared editing will propagate selection across
5223                    equivalent regions, so just use the current region
5224                    selection.
5225                 */
5226
5227                 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5228                         error << _("An error occurred while executing time stretch operation") << endmsg;
5229                 }
5230         }
5231 }
5232
5233 void
5234 TimeFXDrag::aborted (bool)
5235 {
5236         _primary->get_time_axis_view().hide_timestretch ();
5237 }
5238
5239 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5240         : Drag (e, i)
5241 {
5242         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5243 }
5244
5245 void
5246 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5247 {
5248         Drag::start_grab (event);
5249 }
5250
5251 void
5252 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5253 {
5254         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5255 }
5256
5257 void
5258 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5259 {
5260         if (movement_occurred && _editor->session()) {
5261                 /* make sure we stop */
5262                 _editor->session()->request_transport_speed (0.0);
5263         }
5264 }
5265
5266 void
5267 ScrubDrag::aborted (bool)
5268 {
5269         /* XXX: TODO */
5270 }
5271
5272 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5273         : Drag (e, i)
5274         , _operation (o)
5275         , _add (false)
5276         , _track_selection_at_start (e)
5277         , _time_selection_at_start (!_editor->get_selection().time.empty())
5278 {
5279         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5280
5281         if (_time_selection_at_start) {
5282                 start_at_start = _editor->get_selection().time.start();
5283                 end_at_start = _editor->get_selection().time.end_frame();
5284         }
5285 }
5286
5287 void
5288 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5289 {
5290         if (_editor->session() == 0) {
5291                 return;
5292         }
5293
5294         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5295
5296         switch (_operation) {
5297         case CreateSelection:
5298                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5299                         _add = true;
5300                 } else {
5301                         _add = false;
5302                 }
5303                 cursor = _editor->cursors()->selector;
5304                 Drag::start_grab (event, cursor);
5305                 break;
5306
5307         case SelectionStartTrim:
5308                 if (_editor->clicked_axisview) {
5309                         _editor->clicked_axisview->order_selection_trims (_item, true);
5310                 }
5311                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5312                 break;
5313
5314         case SelectionEndTrim:
5315                 if (_editor->clicked_axisview) {
5316                         _editor->clicked_axisview->order_selection_trims (_item, false);
5317                 }
5318                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5319                 break;
5320
5321         case SelectionMove:
5322                 Drag::start_grab (event, cursor);
5323                 break;
5324
5325         case SelectionExtend:
5326                 Drag::start_grab (event, cursor);
5327                 break;
5328         }
5329
5330         if (_operation == SelectionMove) {
5331                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5332         } else {
5333                 show_verbose_cursor_time (adjusted_current_frame (event));
5334         }
5335 }
5336
5337 void
5338 SelectionDrag::setup_pointer_frame_offset ()
5339 {
5340         switch (_operation) {
5341         case CreateSelection:
5342                 _pointer_frame_offset = 0;
5343                 break;
5344
5345         case SelectionStartTrim:
5346         case SelectionMove:
5347                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5348                 break;
5349
5350         case SelectionEndTrim:
5351                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5352                 break;
5353
5354         case SelectionExtend:
5355                 break;
5356         }
5357 }
5358
5359 void
5360 SelectionDrag::motion (GdkEvent* event, bool first_move)
5361 {
5362         framepos_t start = 0;
5363         framepos_t end = 0;
5364         framecnt_t length = 0;
5365         framecnt_t distance = 0;
5366         MusicFrame start_mf (0, 0);
5367         framepos_t const pending_position = adjusted_current_frame (event);
5368
5369         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5370                 return;
5371         }
5372
5373         if (first_move) {
5374                 _track_selection_at_start = _editor->selection->tracks;
5375         }
5376
5377         switch (_operation) {
5378         case CreateSelection:
5379         {
5380                 MusicFrame grab (grab_frame (), 0);
5381                 if (first_move) {
5382                         grab.frame = adjusted_current_frame (event, false);
5383                         if (grab.frame < pending_position) {
5384                                 _editor->snap_to (grab, RoundDownMaybe);
5385                         }  else {
5386                                 _editor->snap_to (grab, RoundUpMaybe);
5387                         }
5388                 }
5389
5390                 if (pending_position < grab.frame) {
5391                         start = pending_position;
5392                         end = grab.frame;
5393                 } else {
5394                         end = pending_position;
5395                         start = grab.frame;
5396                 }
5397
5398                 /* first drag: Either add to the selection
5399                    or create a new selection
5400                 */
5401
5402                 if (first_move) {
5403
5404                         if (_add) {
5405
5406                                 /* adding to the selection */
5407                                 _editor->set_selected_track_as_side_effect (Selection::Add);
5408                                 _editor->clicked_selection = _editor->selection->add (start, end);
5409                                 _add = false;
5410
5411                         } else {
5412
5413                                 /* new selection */
5414
5415                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5416                                         _editor->set_selected_track_as_side_effect (Selection::Set);
5417                                 }
5418
5419                                 _editor->clicked_selection = _editor->selection->set (start, end);
5420                         }
5421                 }
5422
5423                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5424                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5425                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5426                 if (atest) {
5427                         _editor->selection->add (atest);
5428                         break;
5429                 }
5430
5431                 /* select all tracks within the rectangle that we've marked out so far */
5432                 TrackViewList new_selection;
5433                 TrackViewList& all_tracks (_editor->track_views);
5434
5435                 ArdourCanvas::Coord const top = grab_y();
5436                 ArdourCanvas::Coord const bottom = current_pointer_y();
5437
5438                 if (top >= 0 && bottom >= 0) {
5439
5440                         //first, find the tracks that are covered in the y range selection
5441                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5442                                 if ((*i)->covered_by_y_range (top, bottom)) {
5443                                         new_selection.push_back (*i);
5444                                 }
5445                         }
5446
5447                         //now compare our list with the current selection, and add as necessary
5448                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5449                         TrackViewList tracks_to_add;
5450                         TrackViewList tracks_to_remove;
5451                         vector<RouteGroup*> selected_route_groups;
5452
5453                         if (!first_move) {
5454                                 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5455                                         if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5456                                                 tracks_to_remove.push_back (*i);
5457                                         } else {
5458                                                 RouteGroup* rg = (*i)->route_group();
5459                                                 if (rg && rg->is_active() && rg->is_select()) {
5460                                                         selected_route_groups.push_back (rg);
5461                                                 }
5462                                         }
5463                                 }
5464                         }
5465
5466                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5467                                 if (!_editor->selection->tracks.contains (*i)) {
5468                                         tracks_to_add.push_back (*i);
5469                                         RouteGroup* rg = (*i)->route_group();
5470
5471                                         if (rg && rg->is_active() && rg->is_select()) {
5472                                                 selected_route_groups.push_back (rg);
5473                                         }
5474                                 }
5475                         }
5476
5477                         _editor->selection->add (tracks_to_add);
5478
5479                         if (!tracks_to_remove.empty()) {
5480
5481                                 /* check all these to-be-removed tracks against the
5482                                  * possibility that they are selected by being
5483                                  * in the same group as an approved track.
5484                                  */
5485
5486                                 for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
5487                                         RouteGroup* rg = (*i)->route_group();
5488
5489                                         if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
5490                                                 i = tracks_to_remove.erase (i);
5491                                         } else {
5492                                                 ++i;
5493                                         }
5494                                 }
5495
5496                                 /* remove whatever is left */
5497
5498                                 _editor->selection->remove (tracks_to_remove);
5499                         }
5500                 }
5501         }
5502         break;
5503
5504         case SelectionStartTrim:
5505
5506                 end = _editor->selection->time[_editor->clicked_selection].end;
5507
5508                 if (pending_position > end) {
5509                         start = end;
5510                 } else {
5511                         start = pending_position;
5512                 }
5513                 break;
5514
5515         case SelectionEndTrim:
5516
5517                 start = _editor->selection->time[_editor->clicked_selection].start;
5518
5519                 if (pending_position < start) {
5520                         end = start;
5521                 } else {
5522                         end = pending_position;
5523                 }
5524
5525                 break;
5526
5527         case SelectionMove:
5528
5529                 start = _editor->selection->time[_editor->clicked_selection].start;
5530                 end = _editor->selection->time[_editor->clicked_selection].end;
5531
5532                 length = end - start;
5533                 distance = pending_position - start;
5534                 start = pending_position;
5535
5536                 start_mf.frame = start;
5537                 _editor->snap_to (start_mf);
5538
5539                 end = start_mf.frame + length;
5540
5541                 break;
5542
5543         case SelectionExtend:
5544                 break;
5545         }
5546
5547         if (start != end) {
5548                 switch (_operation) {
5549                 case SelectionMove:
5550                         if (_time_selection_at_start) {
5551                                 _editor->selection->move_time (distance);
5552                         }
5553                         break;
5554                 default:
5555                         _editor->selection->replace (_editor->clicked_selection, start, end);
5556                 }
5557         }
5558
5559         if (_operation == SelectionMove) {
5560                 show_verbose_cursor_time(start);
5561         } else {
5562                 show_verbose_cursor_time(pending_position);
5563         }
5564 }
5565
5566 void
5567 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5568 {
5569         Session* s = _editor->session();
5570
5571         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5572         if (movement_occurred) {
5573                 motion (event, false);
5574                 /* XXX this is not object-oriented programming at all. ick */
5575                 if (_editor->selection->time.consolidate()) {
5576                         _editor->selection->TimeChanged ();
5577                 }
5578
5579                 /* XXX what if its a music time selection? */
5580                 if (s) {
5581                         if (s->get_play_range() && s->transport_rolling()) {
5582                                 s->request_play_range (&_editor->selection->time, true);
5583                         } else if (!s->config.get_external_sync()) {
5584                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5585                                         s->request_locate (_editor->get_selection().time.start());
5586                                 }
5587                         }
5588
5589                         if (_editor->get_selection().time.length() != 0) {
5590                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5591                         } else {
5592                                 s->clear_range_selection ();
5593                         }
5594                 }
5595
5596         } else {
5597                 /* just a click, no pointer movement.
5598                  */
5599
5600                 if (was_double_click()) {
5601                         if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5602                                 _editor->temporal_zoom_selection (Both);
5603                                 return;
5604                         }
5605                 }
5606
5607                 if (_operation == SelectionExtend) {
5608                         if (_time_selection_at_start) {
5609                                 framepos_t pos = adjusted_current_frame (event, false);
5610                                 framepos_t start = min (pos, start_at_start);
5611                                 framepos_t end = max (pos, end_at_start);
5612                                 _editor->selection->set (start, end);
5613                         }
5614                 } else {
5615                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5616                                 if (_editor->clicked_selection) {
5617                                         _editor->selection->remove (_editor->clicked_selection);
5618                                 }
5619                         } else {
5620                                 if (!_editor->clicked_selection) {
5621                                         _editor->selection->clear_time();
5622                                 }
5623                         }
5624                 }
5625
5626                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5627                         _editor->selection->set (_editor->clicked_axisview);
5628                 }
5629
5630                 if (s && s->get_play_range () && s->transport_rolling()) {
5631                         s->request_stop (false, false);
5632                 }
5633
5634         }
5635
5636         _editor->stop_canvas_autoscroll ();
5637         _editor->clicked_selection = 0;
5638         _editor->commit_reversible_selection_op ();
5639 }
5640
5641 void
5642 SelectionDrag::aborted (bool)
5643 {
5644         /* XXX: TODO */
5645 }
5646
5647 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5648         : Drag (e, i, false),
5649           _operation (o),
5650           _copy (false)
5651 {
5652         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5653
5654         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5655                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5656                                                                       physical_screen_height (_editor->current_toplevel()->get_window())));
5657         _drag_rect->hide ();
5658
5659         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5660         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5661 }
5662
5663 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5664 {
5665         /* normal canvas items will be cleaned up when their parent group is deleted. But
5666            this item is created as the child of a long-lived parent group, and so we
5667            need to explicitly delete it.
5668         */
5669         delete _drag_rect;
5670 }
5671
5672 void
5673 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5674 {
5675         if (_editor->session() == 0) {
5676                 return;
5677         }
5678
5679         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5680
5681         if (!_editor->temp_location) {
5682                 _editor->temp_location = new Location (*_editor->session());
5683         }
5684
5685         switch (_operation) {
5686         case CreateSkipMarker:
5687         case CreateRangeMarker:
5688         case CreateTransportMarker:
5689         case CreateCDMarker:
5690
5691                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5692                         _copy = true;
5693                 } else {
5694                         _copy = false;
5695                 }
5696                 cursor = _editor->cursors()->selector;
5697                 break;
5698         }
5699
5700         Drag::start_grab (event, cursor);
5701
5702         show_verbose_cursor_time (adjusted_current_frame (event));
5703 }
5704
5705 void
5706 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5707 {
5708         framepos_t start = 0;
5709         framepos_t end = 0;
5710         ArdourCanvas::Rectangle *crect;
5711
5712         switch (_operation) {
5713         case CreateSkipMarker:
5714                 crect = _editor->range_bar_drag_rect;
5715                 break;
5716         case CreateRangeMarker:
5717                 crect = _editor->range_bar_drag_rect;
5718                 break;
5719         case CreateTransportMarker:
5720                 crect = _editor->transport_bar_drag_rect;
5721                 break;
5722         case CreateCDMarker:
5723                 crect = _editor->cd_marker_bar_drag_rect;
5724                 break;
5725         default:
5726                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5727                 return;
5728                 break;
5729         }
5730
5731         framepos_t const pf = adjusted_current_frame (event);
5732
5733         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5734                 MusicFrame grab (grab_frame (), 0);
5735                 _editor->snap_to (grab);
5736
5737                 if (pf < grab_frame()) {
5738                         start = pf;
5739                         end = grab.frame;
5740                 } else {
5741                         end = pf;
5742                         start = grab.frame;
5743                 }
5744
5745                 /* first drag: Either add to the selection
5746                    or create a new selection.
5747                 */
5748
5749                 if (first_move) {
5750
5751                         _editor->temp_location->set (start, end);
5752
5753                         crect->show ();
5754
5755                         update_item (_editor->temp_location);
5756                         _drag_rect->show();
5757                         //_drag_rect->raise_to_top();
5758
5759                 }
5760         }
5761
5762         if (start != end) {
5763                 _editor->temp_location->set (start, end);
5764
5765                 double x1 = _editor->sample_to_pixel (start);
5766                 double x2 = _editor->sample_to_pixel (end);
5767                 crect->set_x0 (x1);
5768                 crect->set_x1 (x2);
5769
5770                 update_item (_editor->temp_location);
5771         }
5772
5773         show_verbose_cursor_time (pf);
5774
5775 }
5776
5777 void
5778 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5779 {
5780         Location * newloc = 0;
5781         string rangename;
5782         int flags;
5783
5784         if (movement_occurred) {
5785                 motion (event, false);
5786                 _drag_rect->hide();
5787
5788                 switch (_operation) {
5789                 case CreateSkipMarker:
5790                 case CreateRangeMarker:
5791                 case CreateCDMarker:
5792                     {
5793                         XMLNode &before = _editor->session()->locations()->get_state();
5794                         if (_operation == CreateSkipMarker) {
5795                                 _editor->begin_reversible_command (_("new skip marker"));
5796                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5797                                 flags = Location::IsRangeMarker | Location::IsSkip;
5798                                 _editor->range_bar_drag_rect->hide();
5799                         } else if (_operation == CreateCDMarker) {
5800                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5801                                 _editor->begin_reversible_command (_("new CD marker"));
5802                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5803                                 _editor->cd_marker_bar_drag_rect->hide();
5804                         } else {
5805                                 _editor->begin_reversible_command (_("new skip marker"));
5806                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5807                                 flags = Location::IsRangeMarker;
5808                                 _editor->range_bar_drag_rect->hide();
5809                         }
5810                         newloc = new Location (
5811                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5812                                 , _editor->get_grid_music_divisions (event->button.state));
5813
5814                         _editor->session()->locations()->add (newloc, true);
5815                         XMLNode &after = _editor->session()->locations()->get_state();
5816                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5817                         _editor->commit_reversible_command ();
5818                         break;
5819                     }
5820
5821                 case CreateTransportMarker:
5822                         // popup menu to pick loop or punch
5823                         _editor->new_transport_marker_context_menu (&event->button, _item);
5824                         break;
5825                 }
5826
5827         } else {
5828
5829                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5830
5831                 if (_operation == CreateTransportMarker) {
5832
5833                         /* didn't drag, so just locate */
5834
5835                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5836
5837                 } else if (_operation == CreateCDMarker) {
5838
5839                         /* didn't drag, but mark is already created so do
5840                          * nothing */
5841
5842                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5843
5844                         framepos_t start;
5845                         framepos_t end;
5846
5847                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5848
5849                         if (end == max_framepos) {
5850                                 end = _editor->session()->current_end_frame ();
5851                         }
5852
5853                         if (start == max_framepos) {
5854                                 start = _editor->session()->current_start_frame ();
5855                         }
5856
5857                         switch (_editor->mouse_mode) {
5858                         case MouseObject:
5859                                 /* find the two markers on either side and then make the selection from it */
5860                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5861                                 break;
5862
5863                         case MouseRange:
5864                                 /* find the two markers on either side of the click and make the range out of it */
5865                                 _editor->selection->set (start, end);
5866                                 break;
5867
5868                         default:
5869                                 break;
5870                         }
5871                 }
5872         }
5873
5874         _editor->stop_canvas_autoscroll ();
5875 }
5876
5877 void
5878 RangeMarkerBarDrag::aborted (bool movement_occurred)
5879 {
5880         if (movement_occurred) {
5881                 _drag_rect->hide ();
5882         }
5883 }
5884
5885 void
5886 RangeMarkerBarDrag::update_item (Location* location)
5887 {
5888         double const x1 = _editor->sample_to_pixel (location->start());
5889         double const x2 = _editor->sample_to_pixel (location->end());
5890
5891         _drag_rect->set_x0 (x1);
5892         _drag_rect->set_x1 (x2);
5893 }
5894
5895 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5896         : Drag (e, i)
5897         , _cumulative_dx (0)
5898         , _cumulative_dy (0)
5899         , _earliest (0.0)
5900         , _was_selected (false)
5901         , _copy (false)
5902 {
5903         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5904
5905         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5906         assert (_primary);
5907         _region = &_primary->region_view ();
5908         _note_height = _region->midi_stream_view()->note_height ();
5909 }
5910
5911 void
5912 NoteDrag::setup_pointer_frame_offset ()
5913 {
5914         _pointer_frame_offset = raw_grab_frame()
5915                 - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5916 }
5917
5918 void
5919 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5920 {
5921         Drag::start_grab (event);
5922
5923         if (ArdourKeyboard::indicates_copy (event->button.state)) {
5924                 _copy = true;
5925         } else {
5926                 _copy = false;
5927         }
5928
5929         setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
5930
5931         if (!(_was_selected = _primary->selected())) {
5932
5933                 /* tertiary-click means extend selection - we'll do that on button release,
5934                    so don't add it here, because otherwise we make it hard to figure
5935                    out the "extend-to" range.
5936                 */
5937
5938                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5939
5940                 if (!extend) {
5941                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5942
5943                         if (add) {
5944                                 _region->note_selected (_primary, true);
5945                         } else {
5946                                 _editor->get_selection().clear_points();
5947                                 _region->unique_select (_primary);
5948                         }
5949                 }
5950         }
5951 }
5952
5953 /** @return Current total drag x change in quarter notes */
5954 double
5955 NoteDrag::total_dx (GdkEvent * event) const
5956 {
5957         if (_x_constrained) {
5958                 return 0;
5959         }
5960
5961         TempoMap& map (_editor->session()->tempo_map());
5962
5963         /* dx in frames */
5964         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5965
5966         /* primary note time */
5967         frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5968
5969         /* primary note time in quarter notes */
5970         double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
5971
5972         /* new time of the primary note in session frames */
5973         frameoffset_t st = n + dx + snap_delta (event->button.state);
5974
5975         /* possibly snap and return corresponding delta in quarter notes */
5976         MusicFrame snap (st, 0);
5977         _editor->snap_to_with_modifier (snap, event);
5978         double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
5979
5980         /* prevent the earliest note being dragged earlier than the region's start position */
5981         if (_earliest + ret < _region->midi_region()->start_beats()) {
5982                 ret -= (_earliest + ret) - _region->midi_region()->start_beats();
5983         }
5984
5985         return ret;
5986 }
5987
5988 /** @return Current total drag y change in note number */
5989 int8_t
5990 NoteDrag::total_dy () const
5991 {
5992         if (_y_constrained) {
5993                 return 0;
5994         }
5995
5996         double const y = _region->midi_view()->y_position ();
5997         /* new current note */
5998         uint8_t n = _region->y_to_note (current_pointer_y () - y);
5999         /* clamp */
6000         MidiStreamView* msv = _region->midi_stream_view ();
6001         n = max (msv->lowest_note(), n);
6002         n = min (msv->highest_note(), n);
6003         /* and work out delta */
6004         return n - _region->y_to_note (grab_y() - y);
6005 }
6006
6007 void
6008 NoteDrag::motion (GdkEvent * event, bool first_move)
6009 {
6010         if (first_move) {
6011                 _earliest = _region->earliest_in_selection().to_double();
6012                 if (_copy) {
6013                         /* make copies of all the selected notes */
6014                         _primary = _region->copy_selection (_primary);
6015                 }
6016         }
6017
6018         /* Total change in x and y since the start of the drag */
6019         double const dx_qn = total_dx (event);
6020         int8_t const dy = total_dy ();
6021
6022         /* Now work out what we have to do to the note canvas items to set this new drag delta */
6023         double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
6024         double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
6025
6026         if (tdx || tdy) {
6027                 _cumulative_dx = dx_qn;
6028                 _cumulative_dy += tdy;
6029
6030                 int8_t note_delta = total_dy();
6031
6032                 if (tdx || tdy) {
6033                         if (_copy) {
6034                                 _region->move_copies (dx_qn, tdy, note_delta);
6035                         } else {
6036                                 _region->move_selection (dx_qn, tdy, note_delta);
6037                         }
6038
6039                         /* the new note value may be the same as the old one, but we
6040                          * don't know what that means because the selection may have
6041                          * involved more than one note and we might be doing something
6042                          * odd with them. so show the note value anyway, always.
6043                          */
6044
6045                         uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
6046
6047                         _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
6048                 }
6049         }
6050 }
6051
6052 void
6053 NoteDrag::finished (GdkEvent* ev, bool moved)
6054 {
6055         if (!moved) {
6056                 /* no motion - select note */
6057
6058                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
6059                     _editor->current_mouse_mode() == Editing::MouseDraw) {
6060
6061                         bool changed = false;
6062
6063                         if (_was_selected) {
6064                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6065                                 if (add) {
6066                                         _region->note_deselected (_primary);
6067                                         changed = true;
6068                                 } else {
6069                                         _editor->get_selection().clear_points();
6070                                         _region->unique_select (_primary);
6071                                         changed = true;
6072                                 }
6073                         } else {
6074                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
6075                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6076
6077                                 if (!extend && !add && _region->selection_size() > 1) {
6078                                         _editor->get_selection().clear_points();
6079                                         _region->unique_select (_primary);
6080                                         changed = true;
6081                                 } else if (extend) {
6082                                         _region->note_selected (_primary, true, true);
6083                                         changed = true;
6084                                 } else {
6085                                         /* it was added during button press */
6086                                         changed = true;
6087
6088                                 }
6089                         }
6090
6091                         if (changed) {
6092                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
6093                                 _editor->commit_reversible_selection_op();
6094                         }
6095                 }
6096         } else {
6097                 _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
6098         }
6099 }
6100
6101 void
6102 NoteDrag::aborted (bool)
6103 {
6104         /* XXX: TODO */
6105 }
6106
6107 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
6108 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
6109         : Drag (editor, atv->base_item ())
6110         , _ranges (r)
6111         , _y_origin (atv->y_position())
6112         , _nothing_to_drag (false)
6113 {
6114         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6115         setup (atv->lines ());
6116 }
6117
6118 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
6119 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
6120         : Drag (editor, rv->get_canvas_group ())
6121         , _ranges (r)
6122         , _y_origin (rv->get_time_axis_view().y_position())
6123         , _nothing_to_drag (false)
6124         , _integral (false)
6125 {
6126         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6127
6128         list<boost::shared_ptr<AutomationLine> > lines;
6129
6130         AudioRegionView*      audio_view;
6131         AutomationRegionView* automation_view;
6132         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
6133                 lines.push_back (audio_view->get_gain_line ());
6134         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
6135                 lines.push_back (automation_view->line ());
6136                 _integral = true;
6137         } else {
6138                 error << _("Automation range drag created for invalid region type") << endmsg;
6139         }
6140
6141         setup (lines);
6142 }
6143
6144 /** @param lines AutomationLines to drag.
6145  *  @param offset Offset from the session start to the points in the AutomationLines.
6146  */
6147 void
6148 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
6149 {
6150         /* find the lines that overlap the ranges being dragged */
6151         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
6152         while (i != lines.end ()) {
6153                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
6154                 ++j;
6155
6156                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
6157
6158                 /* check this range against all the AudioRanges that we are using */
6159                 list<AudioRange>::const_iterator k = _ranges.begin ();
6160                 while (k != _ranges.end()) {
6161                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
6162                                 break;
6163                         }
6164                         ++k;
6165                 }
6166
6167                 /* add it to our list if it overlaps at all */
6168                 if (k != _ranges.end()) {
6169                         Line n;
6170                         n.line = *i;
6171                         n.state = 0;
6172                         n.range = r;
6173                         _lines.push_back (n);
6174                 }
6175
6176                 i = j;
6177         }
6178
6179         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
6180 }
6181
6182 double
6183 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
6184 {
6185         return 1.0 - ((global_y - _y_origin) / line->height());
6186 }
6187
6188 double
6189 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
6190 {
6191         const double v = list->eval(x);
6192         return _integral ? rint(v) : v;
6193 }
6194
6195 void
6196 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6197 {
6198         Drag::start_grab (event, cursor);
6199
6200         /* Get line states before we start changing things */
6201         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6202                 i->state = &i->line->get_state ();
6203                 i->original_fraction = y_fraction (i->line, current_pointer_y());
6204         }
6205
6206         if (_ranges.empty()) {
6207
6208                 /* No selected time ranges: drag all points */
6209                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6210                         uint32_t const N = i->line->npoints ();
6211                         for (uint32_t j = 0; j < N; ++j) {
6212                                 i->points.push_back (i->line->nth (j));
6213                         }
6214                 }
6215
6216         }
6217
6218         if (_nothing_to_drag) {
6219                 return;
6220         }
6221 }
6222
6223 void
6224 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
6225 {
6226         if (_nothing_to_drag && !first_move) {
6227                 return;
6228         }
6229
6230         if (first_move) {
6231                 _editor->begin_reversible_command (_("automation range move"));
6232
6233                 if (!_ranges.empty()) {
6234
6235                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
6236
6237                                 framecnt_t const half = (i->start + i->end) / 2;
6238
6239                                 /* find the line that this audio range starts in */
6240                                 list<Line>::iterator j = _lines.begin();
6241                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
6242                                         ++j;
6243                                 }
6244
6245                                 if (j != _lines.end()) {
6246                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6247
6248                                 /* j is the line that this audio range starts in; fade into it;
6249                                    64 samples length plucked out of thin air.
6250                                 */
6251
6252                                         framepos_t a = i->start + 64;
6253                                         if (a > half) {
6254                                                 a = half;
6255                                         }
6256
6257                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
6258                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
6259
6260                                         XMLNode &before = the_list->get_state();
6261                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6262                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6263
6264                                         if (add_p || add_q) {
6265                                                 _editor->session()->add_command (
6266                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6267                                         }
6268                                 }
6269
6270                                 /* same thing for the end */
6271
6272                                 j = _lines.begin();
6273                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6274                                         ++j;
6275                                 }
6276
6277                                 if (j != _lines.end()) {
6278                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6279
6280                                         /* j is the line that this audio range starts in; fade out of it;
6281                                            64 samples length plucked out of thin air.
6282                                         */
6283
6284                                         framepos_t b = i->end - 64;
6285                                         if (b < half) {
6286                                                 b = half;
6287                                         }
6288
6289                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6290                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6291
6292                                         XMLNode &before = the_list->get_state();
6293                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6294                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6295
6296                                         if (add_p || add_q) {
6297                                                 _editor->session()->add_command (
6298                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6299                                         }
6300                                 }
6301                         }
6302
6303                         _nothing_to_drag = true;
6304
6305                         /* Find all the points that should be dragged and put them in the relevant
6306                            points lists in the Line structs.
6307                         */
6308
6309                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6310
6311                                 uint32_t const N = i->line->npoints ();
6312                                 for (uint32_t j = 0; j < N; ++j) {
6313
6314                                         /* here's a control point on this line */
6315                                         ControlPoint* p = i->line->nth (j);
6316                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6317
6318                                         /* see if it's inside a range */
6319                                         list<AudioRange>::const_iterator k = _ranges.begin ();
6320                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6321                                                 ++k;
6322                                         }
6323
6324                                         if (k != _ranges.end()) {
6325                                                 /* dragging this point */
6326                                                 _nothing_to_drag = false;
6327                                                 i->points.push_back (p);
6328                                         }
6329                                 }
6330                         }
6331                 }
6332
6333                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6334                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6335                 }
6336         }
6337
6338         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6339                 float const f = y_fraction (l->line, current_pointer_y());
6340                 /* we are ignoring x position for this drag, so we can just pass in anything */
6341                 pair<double, float> result;
6342                 uint32_t ignored;
6343                 result = l->line->drag_motion (0, f, true, false, ignored);
6344                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6345         }
6346 }
6347
6348 void
6349 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6350 {
6351         if (_nothing_to_drag || !motion_occurred) {
6352                 return;
6353         }
6354
6355         motion (event, false);
6356         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6357                 i->line->end_drag (false, 0);
6358         }
6359
6360         _editor->commit_reversible_command ();
6361 }
6362
6363 void
6364 AutomationRangeDrag::aborted (bool)
6365 {
6366         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6367                 i->line->reset ();
6368         }
6369 }
6370
6371 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6372         : view (v)
6373         , initial_time_axis_view (itav)
6374 {
6375         /* note that time_axis_view may be null if the regionview was created
6376          * as part of a copy operation.
6377          */
6378         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6379         layer = v->region()->layer ();
6380         initial_y = v->get_canvas_group()->position().y;
6381         initial_playlist = v->region()->playlist ();
6382         initial_position = v->region()->position ();
6383         initial_end = v->region()->position () + v->region()->length ();
6384 }
6385
6386 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6387         : Drag (e, i->canvas_item ())
6388         , _region_view (r)
6389         , _patch_change (i)
6390         , _cumulative_dx (0)
6391 {
6392         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6393                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6394                                                    grab_frame()));
6395 }
6396
6397 void
6398 PatchChangeDrag::motion (GdkEvent* ev, bool)
6399 {
6400         framepos_t f = adjusted_current_frame (ev);
6401         boost::shared_ptr<Region> r = _region_view->region ();
6402         f = max (f, r->position ());
6403         f = min (f, r->last_frame ());
6404
6405         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6406         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6407         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6408         _cumulative_dx = dxu;
6409 }
6410
6411 void
6412 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6413 {
6414         if (!movement_occurred) {
6415                 if (was_double_click()) {
6416                         _region_view->edit_patch_change (_patch_change);
6417                 }
6418                 return;
6419         }
6420
6421         boost::shared_ptr<Region> r (_region_view->region ());
6422         framepos_t f = adjusted_current_frame (ev);
6423         f = max (f, r->position ());
6424         f = min (f, r->last_frame ());
6425
6426         _region_view->move_patch_change (
6427                 *_patch_change,
6428                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6429                 );
6430 }
6431
6432 void
6433 PatchChangeDrag::aborted (bool)
6434 {
6435         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6436 }
6437
6438 void
6439 PatchChangeDrag::setup_pointer_frame_offset ()
6440 {
6441         boost::shared_ptr<Region> region = _region_view->region ();
6442         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6443 }
6444
6445 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6446         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6447         , _region_view (rv)
6448 {
6449
6450 }
6451
6452 void
6453 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6454 {
6455         _region_view->update_drag_selection (
6456                 x1, x2, y1, y2,
6457                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6458 }
6459
6460 void
6461 MidiRubberbandSelectDrag::deselect_things ()
6462 {
6463         /* XXX */
6464 }
6465
6466 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6467         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6468         , _region_view (rv)
6469 {
6470         _vertical_only = true;
6471 }
6472
6473 void
6474 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6475 {
6476         double const y = _region_view->midi_view()->y_position ();
6477
6478         y1 = max (0.0, y1 - y);
6479         y2 = max (0.0, y2 - y);
6480
6481         _region_view->update_vertical_drag_selection (
6482                 y1,
6483                 y2,
6484                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6485                 );
6486 }
6487
6488 void
6489 MidiVerticalSelectDrag::deselect_things ()
6490 {
6491         /* XXX */
6492 }
6493
6494 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6495         : RubberbandSelectDrag (e, i)
6496 {
6497
6498 }
6499
6500 void
6501 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6502 {
6503         if (drag_in_progress) {
6504                 /* We just want to select things at the end of the drag, not during it */
6505                 return;
6506         }
6507
6508         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6509
6510         _editor->begin_reversible_selection_op (X_("rubberband selection"));
6511
6512         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6513
6514         _editor->commit_reversible_selection_op ();
6515 }
6516
6517 void
6518 EditorRubberbandSelectDrag::deselect_things ()
6519 {
6520         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6521
6522         _editor->selection->clear_tracks();
6523         _editor->selection->clear_regions();
6524         _editor->selection->clear_points ();
6525         _editor->selection->clear_lines ();
6526         _editor->selection->clear_midi_notes ();
6527
6528         _editor->commit_reversible_selection_op();
6529 }
6530
6531 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6532         : Drag (e, i)
6533         , _region_view (rv)
6534         , _drag_rect (0)
6535 {
6536         _note[0] = _note[1] = 0;
6537 }
6538
6539 NoteCreateDrag::~NoteCreateDrag ()
6540 {
6541         delete _drag_rect;
6542 }
6543
6544 framecnt_t
6545 NoteCreateDrag::grid_frames (framepos_t t) const
6546 {
6547
6548         const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6549         const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6550
6551         return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6552                 - _region_view->region_beats_to_region_frames (t_beats);
6553 }
6554
6555 void
6556 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6557 {
6558         Drag::start_grab (event, cursor);
6559
6560         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6561         TempoMap& map (_editor->session()->tempo_map());
6562
6563         const framepos_t pf = _drags->current_pointer_frame ();
6564         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6565
6566         const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6567
6568         double eqaf = map.exact_qn_at_frame (pf, divisions);
6569
6570         if (divisions != 0) {
6571
6572                 const double qaf = map.quarter_note_at_frame (pf);
6573
6574                 /* Hack so that we always snap to the note that we are over, instead of snapping
6575                    to the next one if we're more than halfway through the one we're over.
6576                 */
6577
6578                 const double rem = eqaf - qaf;
6579                 if (rem >= 0.0) {
6580                         eqaf -= grid_beats.to_double();
6581                 }
6582         }
6583
6584         _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6585         /* minimum initial length is grid beats */
6586         _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6587
6588         double const x0 = _editor->sample_to_pixel (_note[0]);
6589         double const x1 = _editor->sample_to_pixel (_note[1]);
6590         double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6591
6592         _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6593         _drag_rect->set_outline_all ();
6594         _drag_rect->set_outline_color (0xffffff99);
6595         _drag_rect->set_fill_color (0xffffff66);
6596 }
6597
6598 void
6599 NoteCreateDrag::motion (GdkEvent* event, bool)
6600 {
6601         TempoMap& map (_editor->session()->tempo_map());
6602         const framepos_t pf = _drags->current_pointer_frame ();
6603         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6604         double eqaf = map.exact_qn_at_frame (pf, divisions);
6605
6606         if (divisions != 0) {
6607
6608                 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6609
6610                 const double qaf = map.quarter_note_at_frame (pf);
6611                 /* Hack so that we always snap to the note that we are over, instead of snapping
6612                    to the next one if we're more than halfway through the one we're over.
6613                 */
6614
6615                 const double rem = eqaf - qaf;
6616                 if (rem >= 0.0) {
6617                         eqaf -= grid_beats.to_double();
6618                 }
6619
6620                 eqaf += grid_beats.to_double();
6621         }
6622         _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6623
6624         double const x0 = _editor->sample_to_pixel (_note[0]);
6625         double const x1 = _editor->sample_to_pixel (_note[1]);
6626         _drag_rect->set_x0 (std::min(x0, x1));
6627         _drag_rect->set_x1 (std::max(x0, x1));
6628 }
6629
6630 void
6631 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6632 {
6633         /* we create a note even if there was no movement */
6634         framepos_t const start = min (_note[0], _note[1]);
6635         framepos_t const start_sess_rel = start + _region_view->region()->position();
6636         framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6637         framecnt_t const g = grid_frames (start);
6638
6639         if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6640                 length = g;
6641         }
6642
6643         TempoMap& map (_editor->session()->tempo_map());
6644         const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6645         Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6646
6647         _editor->begin_reversible_command (_("Create Note"));
6648         _region_view->clear_editor_note_selection();
6649         _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6650         _editor->commit_reversible_command ();
6651 }
6652
6653 double
6654 NoteCreateDrag::y_to_region (double y) const
6655 {
6656         double x = 0;
6657         _region_view->get_canvas_group()->canvas_to_item (x, y);
6658         return y;
6659 }
6660
6661 void
6662 NoteCreateDrag::aborted (bool)
6663 {
6664
6665 }
6666
6667 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6668         : Drag (e, i)
6669         , _region_view (rv)
6670         , _last_pos (0)
6671         , _y (0.0)
6672 {
6673 }
6674
6675 HitCreateDrag::~HitCreateDrag ()
6676 {
6677 }
6678
6679 void
6680 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6681 {
6682         Drag::start_grab (event, cursor);
6683
6684         TempoMap& map (_editor->session()->tempo_map());
6685
6686         _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6687
6688         const framepos_t pf = _drags->current_pointer_frame ();
6689         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6690
6691         const double eqaf = map.exact_qn_at_frame (pf, divisions);
6692
6693         boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6694
6695         if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6696                 return;
6697         }
6698
6699         const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6700         Evoral::Beats length = _region_view->get_grid_beats (pf);
6701
6702         _editor->begin_reversible_command (_("Create Hit"));
6703         _region_view->clear_editor_note_selection();
6704         _region_view->create_note_at (start, _y, length, event->button.state, false);
6705
6706         _last_pos = start;
6707 }
6708
6709 void
6710 HitCreateDrag::motion (GdkEvent* event, bool)
6711 {
6712         TempoMap& map (_editor->session()->tempo_map());
6713
6714         const framepos_t pf = _drags->current_pointer_frame ();
6715         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6716
6717         if (divisions == 0) {
6718                 return;
6719         }
6720
6721         const double eqaf = map.exact_qn_at_frame (pf, divisions);
6722         const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6723
6724         if (_last_pos == start) {
6725                 return;
6726         }
6727
6728         Evoral::Beats length = _region_view->get_grid_beats (pf);
6729
6730         boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6731
6732         if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6733                 return;
6734         }
6735
6736         _region_view->create_note_at (start, _y, length, event->button.state, false);
6737
6738         _last_pos = start;
6739 }
6740
6741 void
6742 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6743 {
6744         _editor->commit_reversible_command ();
6745
6746 }
6747
6748 double
6749 HitCreateDrag::y_to_region (double y) const
6750 {
6751         double x = 0;
6752         _region_view->get_canvas_group()->canvas_to_item (x, y);
6753         return y;
6754 }
6755
6756 void
6757 HitCreateDrag::aborted (bool)
6758 {
6759         // umm..
6760 }
6761
6762 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6763         : Drag (e, i)
6764         , arv (rv)
6765         , start (start_yn)
6766 {
6767         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
6768 }
6769
6770 void
6771 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6772 {
6773         Drag::start_grab (event, cursor);
6774 }
6775
6776 void
6777 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6778 {
6779         double distance;
6780         double new_length;
6781         framecnt_t len;
6782
6783         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6784
6785         if (start) {
6786                 distance = _drags->current_pointer_x() - grab_x();
6787                 len = ar->fade_in()->back()->when;
6788         } else {
6789                 distance = grab_x() - _drags->current_pointer_x();
6790                 len = ar->fade_out()->back()->when;
6791         }
6792
6793         /* how long should it be ? */
6794
6795         new_length = len + _editor->pixel_to_sample (distance);
6796
6797         /* now check with the region that this is legal */
6798
6799         new_length = ar->verify_xfade_bounds (new_length, start);
6800
6801         if (start) {
6802                 arv->reset_fade_in_shape_width (ar, new_length);
6803         } else {
6804                 arv->reset_fade_out_shape_width (ar, new_length);
6805         }
6806 }
6807
6808 void
6809 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6810 {
6811         double distance;
6812         double new_length;
6813         framecnt_t len;
6814
6815         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6816
6817         if (start) {
6818                 distance = _drags->current_pointer_x() - grab_x();
6819                 len = ar->fade_in()->back()->when;
6820         } else {
6821                 distance = grab_x() - _drags->current_pointer_x();
6822                 len = ar->fade_out()->back()->when;
6823         }
6824
6825         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6826
6827         _editor->begin_reversible_command ("xfade trim");
6828         ar->playlist()->clear_owned_changes ();
6829
6830         if (start) {
6831                 ar->set_fade_in_length (new_length);
6832         } else {
6833                 ar->set_fade_out_length (new_length);
6834         }
6835
6836         /* Adjusting the xfade may affect other regions in the playlist, so we need
6837            to get undo Commands from the whole playlist rather than just the
6838            region.
6839         */
6840
6841         vector<Command*> cmds;
6842         ar->playlist()->rdiff (cmds);
6843         _editor->session()->add_commands (cmds);
6844         _editor->commit_reversible_command ();
6845
6846 }
6847
6848 void
6849 CrossfadeEdgeDrag::aborted (bool)
6850 {
6851         if (start) {
6852                 // arv->redraw_start_xfade ();
6853         } else {
6854                 // arv->redraw_end_xfade ();
6855         }
6856 }
6857
6858 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6859         : Drag (e, item, true)
6860         , line (new EditorCursor (*e))
6861 {
6862         line->set_position (pos);
6863         line->show ();
6864         line->track_canvas_item().reparent (_editor->_drag_motion_group);
6865 }
6866
6867 RegionCutDrag::~RegionCutDrag ()
6868 {
6869         delete line;
6870 }
6871
6872 void
6873 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6874 {
6875         Drag::start_grab (event, c);
6876         motion (event, false);
6877 }
6878
6879 void
6880 RegionCutDrag::motion (GdkEvent* event, bool)
6881 {
6882         MusicFrame pos (_drags->current_pointer_frame(), 0);
6883         _editor->snap_to_with_modifier (pos, event);
6884
6885         line->set_position (pos.frame);
6886 }
6887
6888 void
6889 RegionCutDrag::finished (GdkEvent* event, bool)
6890 {
6891         _editor->get_track_canvas()->canvas()->re_enter();
6892
6893
6894         MusicFrame pos (_drags->current_pointer_frame(), 0);
6895         _editor->snap_to_with_modifier (pos, event);
6896         line->hide ();
6897
6898         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
6899
6900         if (rs.empty()) {
6901                 return;
6902         }
6903
6904         _editor->split_regions_at (pos, rs, false);
6905 }
6906
6907 void
6908 RegionCutDrag::aborted (bool)
6909 {
6910 }
6911
6912 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6913         : Drag (e, item, true)
6914 {
6915 }
6916
6917 void
6918 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6919 {
6920         Drag::start_grab (event, c);
6921
6922         framepos_t where = _editor->canvas_event_sample(event);
6923
6924         _editor->_dragging_playhead = true;
6925
6926         _editor->playhead_cursor->set_position (where);
6927 }
6928
6929 void
6930 RulerZoomDrag::motion (GdkEvent* event, bool)
6931 {
6932         framepos_t where = _editor->canvas_event_sample(event);
6933
6934         _editor->playhead_cursor->set_position (where);
6935
6936         const double movement_limit = 20.0;
6937         const double scale = 1.08;
6938         const double y_delta = last_pointer_y() - current_pointer_y();
6939
6940         if (y_delta > 0 && y_delta < movement_limit) {
6941                 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6942         } else if (y_delta < 0 && y_delta > -movement_limit) {
6943                 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6944         }
6945 }
6946
6947 void
6948 RulerZoomDrag::finished (GdkEvent*, bool)
6949 {
6950         _editor->_dragging_playhead = false;
6951
6952         Session* s = _editor->session ();
6953         if (s) {
6954                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6955                 _editor->_pending_locate_request = true;
6956         }
6957
6958 }
6959
6960 void
6961 RulerZoomDrag::aborted (bool)
6962 {
6963 }