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