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