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