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