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