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