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