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