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