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