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