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