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