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