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