globally remove all trailing whitespace from ardour code base.
[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         /* select this note; if it is already selected, preserve the existing selection,
2385            otherwise make this note the only one selected.
2386         */
2387         region->note_selected (cnote, cnote->selected ());
2388 }
2389
2390 void
2391 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2392 {
2393         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2394         if (first_move) {
2395                 _editor->begin_reversible_command (_("resize notes"));
2396
2397                 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2398                         MidiRegionSelection::iterator next;
2399                         next = r;
2400                         ++next;
2401                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2402                         if (mrv) {
2403                                 mrv->begin_resizing (at_front);
2404                         }
2405                         r = next;
2406                 }
2407         }
2408
2409         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2410                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2411                 assert (nb);
2412                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2413                 if (mrv) {
2414                         double sd = 0.0;
2415                         bool snap = true;
2416                         bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2417
2418                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2419                                 if (_editor->snap_mode () != SnapOff) {
2420                                         snap = false;
2421                                 }
2422                         } else {
2423                                 if (_editor->snap_mode () == SnapOff) {
2424                                         snap = false;
2425                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2426                                         if (apply_snap_delta) {
2427                                                 snap = true;
2428                                         }
2429                                 }
2430                         }
2431
2432                         if (apply_snap_delta) {
2433                                 sd = _snap_delta;
2434                         }
2435
2436                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2437                 }
2438         }
2439 }
2440
2441 void
2442 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2443 {
2444         if (!movement_occurred) {
2445                 return;
2446         }
2447
2448         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2449         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2450                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2451                 assert (nb);
2452                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2453                 double sd = 0.0;
2454                 bool snap = true;
2455                 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2456                 if (mrv) {
2457                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2458                                 if (_editor->snap_mode () != SnapOff) {
2459                                         snap = false;
2460                                 }
2461                         } else {
2462                                 if (_editor->snap_mode () == SnapOff) {
2463                                         snap = false;
2464                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2465                                         if (apply_snap_delta) {
2466                                                 snap = true;
2467                                         }
2468                                 }
2469                         }
2470
2471                         if (apply_snap_delta) {
2472                                 sd = _snap_delta;
2473                         }
2474
2475                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2476                 }
2477         }
2478
2479         _editor->commit_reversible_command ();
2480 }
2481
2482 void
2483 NoteResizeDrag::aborted (bool)
2484 {
2485         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2486         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2487                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2488                 if (mrv) {
2489                         mrv->abort_resizing ();
2490                 }
2491         }
2492 }
2493
2494 AVDraggingView::AVDraggingView (RegionView* v)
2495         : view (v)
2496 {
2497         initial_position = v->region()->position ();
2498 }
2499
2500 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2501         : Drag (e, i)
2502 {
2503         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2504
2505         RegionSelection rs;
2506         TrackViewList empty;
2507         empty.clear();
2508         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2509         std::list<RegionView*> views = rs.by_layer();
2510
2511         _stuck = false;
2512         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2513                 RegionView* rv = (*i);
2514                 if (!rv->region()->video_locked()) {
2515                         continue;
2516                 }
2517                 if (rv->region()->locked()) {
2518                         _stuck = true;
2519                 }
2520                 _views.push_back (AVDraggingView (rv));
2521         }
2522 }
2523
2524 void
2525 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2526 {
2527         Drag::start_grab (event);
2528         if (_editor->session() == 0) {
2529                 return;
2530         }
2531
2532         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2533                 _stuck = false;
2534                 _views.clear();
2535         }
2536
2537         if (_stuck) {
2538                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2539                 return;
2540         }
2541
2542         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2543         _max_backwards_drag = (
2544                           ARDOUR_UI::instance()->video_timeline->get_duration()
2545                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2546                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2547                         );
2548
2549         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2550                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2551                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2552                 }
2553         }
2554         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2555
2556         char buf[128];
2557         Timecode::Time timecode;
2558         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2559         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);
2560         show_verbose_cursor_text (buf);
2561 }
2562
2563 void
2564 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2565 {
2566         if (_editor->session() == 0) {
2567                 return;
2568         }
2569         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2570                 return;
2571         }
2572         if (_stuck) {
2573                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2574                 return;
2575         }
2576
2577         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2578         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2579
2580         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2581                 dt = - _max_backwards_drag;
2582         }
2583
2584         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2585         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2586
2587         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2588                 RegionView* rv = i->view;
2589                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2590                 if (first_move) {
2591                         rv->drag_start ();
2592                         rv->region()->clear_changes ();
2593                         rv->region()->suspend_property_changes();
2594                 }
2595                 rv->region()->set_position(i->initial_position + dt);
2596                 rv->region_changed(ARDOUR::Properties::position);
2597         }
2598
2599         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2600         Timecode::Time timecode;
2601         Timecode::Time timediff;
2602         char buf[128];
2603         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2604         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2605         snprintf (buf, sizeof (buf),
2606                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2607                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2608                         , _("Video Start:"),
2609                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2610                         , _("Diff:"),
2611                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2612                                 );
2613         show_verbose_cursor_text (buf);
2614 }
2615
2616 void
2617 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2618 {
2619         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2620                 return;
2621         }
2622         if (_stuck) {
2623                 return;
2624         }
2625
2626         if (!movement_occurred || ! _editor->session()) {
2627                 return;
2628         }
2629
2630         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2631
2632         _editor->begin_reversible_command (_("Move Video"));
2633
2634         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2635         ARDOUR_UI::instance()->video_timeline->save_undo();
2636         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2637         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2638
2639         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2640                 i->view->drag_end();
2641                 i->view->region()->resume_property_changes ();
2642
2643                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2644         }
2645
2646         _editor->session()->maybe_update_session_range(
2647                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2648                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2649                         );
2650
2651
2652         _editor->commit_reversible_command ();
2653 }
2654
2655 void
2656 VideoTimeLineDrag::aborted (bool)
2657 {
2658         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2659                 return;
2660         }
2661         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2662         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2663
2664         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2665                 i->view->region()->resume_property_changes ();
2666                 i->view->region()->set_position(i->initial_position);
2667         }
2668 }
2669
2670 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2671         : RegionDrag (e, i, p, v)
2672         , _preserve_fade_anchor (preserve_fade_anchor)
2673         , _jump_position_when_done (false)
2674 {
2675         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2676 }
2677
2678 void
2679 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2680 {
2681         double speed = 1.0;
2682         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2683         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2684
2685         if (tv && tv->is_track()) {
2686                 speed = tv->track()->speed();
2687         }
2688
2689         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2690         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2691         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2692
2693         framepos_t const pf = adjusted_current_frame (event);
2694         setup_snap_delta (region_start);
2695
2696         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2697                 /* Move the contents of the region around without changing the region bounds */
2698                 _operation = ContentsTrim;
2699                 Drag::start_grab (event, _editor->cursors()->trimmer);
2700         } else {
2701                 /* These will get overridden for a point trim.*/
2702                 if (pf < (region_start + region_length/2)) {
2703                         /* closer to front */
2704                         _operation = StartTrim;
2705                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2706                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2707                         } else {
2708                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2709                         }
2710                 } else {
2711                         /* closer to end */
2712                         _operation = EndTrim;
2713                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2714                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2715                         } else {
2716                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2717                         }
2718                 }
2719         }
2720         /* jump trim disabled for now
2721         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2722                 _jump_position_when_done = true;
2723         }
2724         */
2725
2726         switch (_operation) {
2727         case StartTrim:
2728                 show_verbose_cursor_time (region_start);
2729                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2730                         i->view->trim_front_starting ();
2731                 }
2732                 break;
2733         case EndTrim:
2734                 show_verbose_cursor_duration (region_start, region_end);
2735                 break;
2736         case ContentsTrim:
2737                 show_verbose_cursor_time (pf);
2738                 break;
2739         }
2740
2741         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2742                 i->view->region()->suspend_property_changes ();
2743         }
2744 }
2745
2746 void
2747 TrimDrag::motion (GdkEvent* event, bool first_move)
2748 {
2749         RegionView* rv = _primary;
2750
2751         double speed = 1.0;
2752         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2753         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2754         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2755         frameoffset_t frame_delta = 0;
2756
2757         if (tv && tv->is_track()) {
2758                 speed = tv->track()->speed();
2759         }
2760         framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2761         framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2762
2763         if (first_move) {
2764
2765                 string trim_type;
2766
2767                 switch (_operation) {
2768                 case StartTrim:
2769                         trim_type = "Region start trim";
2770                         break;
2771                 case EndTrim:
2772                         trim_type = "Region end trim";
2773                         break;
2774                 case ContentsTrim:
2775                         trim_type = "Region content trim";
2776                         break;
2777                 default:
2778                         assert(0);
2779                         break;
2780                 }
2781
2782                 _editor->begin_reversible_command (trim_type);
2783
2784                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2785                         RegionView* rv = i->view;
2786                         rv->enable_display (false);
2787                         rv->region()->playlist()->clear_owned_changes ();
2788
2789                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2790
2791                         if (arv) {
2792                                 arv->temporarily_hide_envelope ();
2793                                 arv->drag_start ();
2794                         }
2795
2796                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2797                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2798
2799                         if (insert_result.second) {
2800                                 pl->freeze();
2801                         }
2802                 }
2803         }
2804
2805         bool non_overlap_trim = false;
2806
2807         if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2808                 non_overlap_trim = true;
2809         }
2810
2811         /* contstrain trim to fade length */
2812         if (_preserve_fade_anchor) {
2813                 switch (_operation) {
2814                         case StartTrim:
2815                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2816                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2817                                         if (!arv) continue;
2818                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2819                                         if (ar->locked()) continue;
2820                                         framecnt_t len = ar->fade_in()->back()->when;
2821                                         if (len < dt) dt = min(dt, len);
2822                                 }
2823                                 break;
2824                         case EndTrim:
2825                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2826                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2827                                         if (!arv) continue;
2828                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2829                                         if (ar->locked()) continue;
2830                                         framecnt_t len = ar->fade_out()->back()->when;
2831                                         if (len < -dt) dt = max(dt, -len);
2832                                 }
2833                                 break;
2834                         case ContentsTrim:
2835                                 break;
2836                 }
2837         }
2838
2839
2840         switch (_operation) {
2841         case StartTrim:
2842                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2843                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2844                         if (changed && _preserve_fade_anchor) {
2845                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2846                                 if (arv) {
2847                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2848                                         framecnt_t len = ar->fade_in()->back()->when;
2849                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2850                                         framepos_t new_length = len - diff;
2851                                         i->anchored_fade_length = min (ar->length(), new_length);
2852                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2853                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2854                                 }
2855                         }
2856                 }
2857                 break;
2858
2859         case EndTrim:
2860                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2861                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2862                         if (changed && _preserve_fade_anchor) {
2863                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2864                                 if (arv) {
2865                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2866                                         framecnt_t len = ar->fade_out()->back()->when;
2867                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2868                                         framepos_t new_length = len + diff;
2869                                         i->anchored_fade_length = min (ar->length(), new_length);
2870                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2871                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2872                                 }
2873                         }
2874                 }
2875                 break;
2876
2877         case ContentsTrim:
2878                 {
2879                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2880
2881                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2882                                 i->view->move_contents (frame_delta);
2883                         }
2884                 }
2885                 break;
2886         }
2887
2888         switch (_operation) {
2889         case StartTrim:
2890                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2891                 break;
2892         case EndTrim:
2893                 show_verbose_cursor_duration ((framepos_t)  rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2894                 break;
2895         case ContentsTrim:
2896                 // show_verbose_cursor_time (frame_delta);
2897                 break;
2898         }
2899 }
2900
2901 void
2902 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2903 {
2904         if (movement_occurred) {
2905                 motion (event, false);
2906
2907                 if (_operation == StartTrim) {
2908                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2909                                 {
2910                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2911                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2912                                            needs to be zero in the undo record.
2913                                         */
2914                                         i->view->trim_front_ending ();
2915                                 }
2916                                 if (_preserve_fade_anchor) {
2917                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2918                                         if (arv) {
2919                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2920                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2921                                                 ar->set_fade_in_length(i->anchored_fade_length);
2922                                                 ar->set_fade_in_active(true);
2923                                         }
2924                                 }
2925                                 if (_jump_position_when_done) {
2926                                         i->view->region()->set_position (i->initial_position);
2927                                 }
2928                         }
2929                 } else if (_operation == EndTrim) {
2930                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2931                                 if (_preserve_fade_anchor) {
2932                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2933                                         if (arv) {
2934                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2935                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2936                                                 ar->set_fade_out_length(i->anchored_fade_length);
2937                                                 ar->set_fade_out_active(true);
2938                                         }
2939                                 }
2940                                 if (_jump_position_when_done) {
2941                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
2942                                 }
2943                         }
2944                 }
2945
2946                 if (!_views.empty()) {
2947                         if (_operation == StartTrim) {
2948                                 _editor->maybe_locate_with_edit_preroll(
2949                                         _views.begin()->view->region()->position());
2950                         }
2951                         if (_operation == EndTrim) {
2952                                 _editor->maybe_locate_with_edit_preroll(
2953                                         _views.begin()->view->region()->position() +
2954                                         _views.begin()->view->region()->length());
2955                         }
2956                 }
2957
2958                 if (!_editor->selection->selected (_primary)) {
2959                         _primary->thaw_after_trim ();
2960                 } else {
2961
2962                         set<boost::shared_ptr<Playlist> > diffed_playlists;
2963
2964                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2965                                 i->view->thaw_after_trim ();
2966                                 i->view->enable_display (true);
2967
2968                                 /* Trimming one region may affect others on the playlist, so we need
2969                                    to get undo Commands from the whole playlist rather than just the
2970                                    region.  Use diffed_playlists to make sure we don't diff a given
2971                                    playlist more than once.
2972                                 */
2973                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2974                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2975                                         vector<Command*> cmds;
2976                                         p->rdiff (cmds);
2977                                         _editor->session()->add_commands (cmds);
2978                                         diffed_playlists.insert (p);
2979                                 }
2980                         }
2981                 }
2982
2983                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2984                         (*p)->thaw ();
2985                 }
2986
2987                 _editor->motion_frozen_playlists.clear ();
2988                 _editor->commit_reversible_command();
2989
2990         } else {
2991                 /* no mouse movement */
2992                 _editor->point_trim (event, adjusted_current_frame (event));
2993         }
2994
2995         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2996                 if (_operation == StartTrim) {
2997                         i->view->trim_front_ending ();
2998                 }
2999
3000                 i->view->region()->resume_property_changes ();
3001         }
3002 }
3003
3004 void
3005 TrimDrag::aborted (bool movement_occurred)
3006 {
3007         /* Our motion method is changing model state, so use the Undo system
3008            to cancel.  Perhaps not ideal, as this will leave an Undo point
3009            behind which may be slightly odd from the user's point of view.
3010         */
3011
3012         finished (0, true);
3013
3014         if (movement_occurred) {
3015                 _editor->undo ();
3016         }
3017
3018         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3019                 i->view->region()->resume_property_changes ();
3020         }
3021 }
3022
3023 void
3024 TrimDrag::setup_pointer_frame_offset ()
3025 {
3026         list<DraggingView>::iterator i = _views.begin ();
3027         while (i != _views.end() && i->view != _primary) {
3028                 ++i;
3029         }
3030
3031         if (i == _views.end()) {
3032                 return;
3033         }
3034
3035         switch (_operation) {
3036         case StartTrim:
3037                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3038                 break;
3039         case EndTrim:
3040                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3041                 break;
3042         case ContentsTrim:
3043                 break;
3044         }
3045 }
3046
3047 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3048         : Drag (e, i),
3049           _copy (c)
3050 {
3051         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3052         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3053         assert (_marker);
3054 }
3055
3056 void
3057 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3058 {
3059         Drag::start_grab (event, cursor);
3060         show_verbose_cursor_time (adjusted_current_frame(event));
3061 }
3062
3063 void
3064 MeterMarkerDrag::setup_pointer_frame_offset ()
3065 {
3066         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3067 }
3068
3069 void
3070 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3071 {
3072         if (!_marker->meter().movable()) {
3073                 return;
3074         }
3075
3076         if (first_move) {
3077
3078                 // create a dummy marker for visual representation of moving the
3079                 // section, because whether its a copy or not, we're going to
3080                 // leave or lose the original marker (leave if its a copy; lose if its
3081                 // not, because we'll remove it from the map).
3082
3083                 MeterSection section (_marker->meter());
3084
3085                 if (!section.movable()) {
3086                         return;
3087                 }
3088
3089                 char name[64];
3090                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3091
3092                 _marker = new MeterMarker (
3093                         *_editor,
3094                         *_editor->meter_group,
3095                         UIConfiguration::instance().color ("meter marker"),
3096                         name,
3097                         *new MeterSection (_marker->meter())
3098                 );
3099
3100                 /* use the new marker for the grab */
3101                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3102
3103                 if (!_copy) {
3104                         TempoMap& map (_editor->session()->tempo_map());
3105                         /* get current state */
3106                         before_state = &map.get_state();
3107                         /* remove the section while we drag it */
3108                         map.remove_meter (section, true);
3109                 }
3110         }
3111
3112         framepos_t const pf = adjusted_current_frame (event);
3113
3114         _marker->set_position (pf);
3115         show_verbose_cursor_time (pf);
3116 }
3117
3118 void
3119 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3120 {
3121         if (!movement_occurred) {
3122                 if (was_double_click()) {
3123                         _editor->edit_meter_marker (*_marker);
3124                 }
3125                 return;
3126         }
3127
3128         if (!_marker->meter().movable()) {
3129                 return;
3130         }
3131
3132         motion (event, false);
3133
3134         Timecode::BBT_Time when;
3135
3136         TempoMap& map (_editor->session()->tempo_map());
3137         map.bbt_time (last_pointer_frame(), when);
3138
3139         if (_copy == true) {
3140                 _editor->begin_reversible_command (_("copy meter mark"));
3141                 XMLNode &before = map.get_state();
3142                 map.add_meter (_marker->meter(), when);
3143                 XMLNode &after = map.get_state();
3144                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3145                 _editor->commit_reversible_command ();
3146
3147         } else {
3148                 _editor->begin_reversible_command (_("move meter mark"));
3149
3150                 /* we removed it before, so add it back now */
3151
3152                 map.add_meter (_marker->meter(), when);
3153                 XMLNode &after = map.get_state();
3154                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3155                 _editor->commit_reversible_command ();
3156         }
3157
3158         // delete the dummy marker we used for visual representation while moving.
3159         // a new visual marker will show up automatically.
3160         delete _marker;
3161 }
3162
3163 void
3164 MeterMarkerDrag::aborted (bool moved)
3165 {
3166         _marker->set_position (_marker->meter().frame ());
3167
3168         if (moved) {
3169                 TempoMap& map (_editor->session()->tempo_map());
3170                 /* we removed it before, so add it back now */
3171                 map.add_meter (_marker->meter(), _marker->meter().frame());
3172                 // delete the dummy marker we used for visual representation while moving.
3173                 // a new visual marker will show up automatically.
3174                 delete _marker;
3175         }
3176 }
3177
3178 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3179         : Drag (e, i),
3180           _copy (c)
3181 {
3182         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3183
3184         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3185         assert (_marker);
3186 }
3187
3188 void
3189 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3190 {
3191         Drag::start_grab (event, cursor);
3192         show_verbose_cursor_time (adjusted_current_frame (event));
3193 }
3194
3195 void
3196 TempoMarkerDrag::setup_pointer_frame_offset ()
3197 {
3198         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3199 }
3200
3201 void
3202 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3203 {
3204         if (!_marker->tempo().movable()) {
3205                 return;
3206         }
3207
3208         if (first_move) {
3209
3210                 // create a dummy marker for visual representation of moving the
3211                 // section, because whether its a copy or not, we're going to
3212                 // leave or lose the original marker (leave if its a copy; lose if its
3213                 // not, because we'll remove it from the map).
3214
3215                 // create a dummy marker for visual representation of moving the copy.
3216                 // The actual copying is not done before we reach the finish callback.
3217
3218                 char name[64];
3219                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3220
3221                 TempoSection section (_marker->tempo());
3222
3223                 _marker = new TempoMarker (
3224                         *_editor,
3225                         *_editor->tempo_group,
3226                         UIConfiguration::instance().color ("tempo marker"),
3227                         name,
3228                         *new TempoSection (_marker->tempo())
3229                         );
3230
3231                 /* use the new marker for the grab */
3232                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3233
3234                 if (!_copy) {
3235                         TempoMap& map (_editor->session()->tempo_map());
3236                         /* get current state */
3237                         before_state = &map.get_state();
3238                         /* remove the section while we drag it */
3239                         map.remove_tempo (section, true);
3240                 }
3241         }
3242
3243         framepos_t const pf = adjusted_current_frame (event);
3244         _marker->set_position (pf);
3245         show_verbose_cursor_time (pf);
3246 }
3247
3248 void
3249 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3250 {
3251         if (!movement_occurred) {
3252                 if (was_double_click()) {
3253                         _editor->edit_tempo_marker (*_marker);
3254                 }
3255                 return;
3256         }
3257
3258         if (!_marker->tempo().movable()) {
3259                 return;
3260         }
3261
3262         motion (event, false);
3263
3264         TempoMap& map (_editor->session()->tempo_map());
3265         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3266         Timecode::BBT_Time when;
3267
3268         map.bbt_time (beat_time, when);
3269
3270         if (_copy == true) {
3271                 _editor->begin_reversible_command (_("copy tempo mark"));
3272                 XMLNode &before = map.get_state();
3273                 map.add_tempo (_marker->tempo(), when);
3274                 XMLNode &after = map.get_state();
3275                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3276                 _editor->commit_reversible_command ();
3277
3278         } else {
3279                 _editor->begin_reversible_command (_("move tempo mark"));
3280                 /* we removed it before, so add it back now */
3281                 map.add_tempo (_marker->tempo(), when);
3282                 XMLNode &after = map.get_state();
3283                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3284                 _editor->commit_reversible_command ();
3285         }
3286
3287         // delete the dummy marker we used for visual representation while moving.
3288         // a new visual marker will show up automatically.
3289         delete _marker;
3290 }
3291
3292 void
3293 TempoMarkerDrag::aborted (bool moved)
3294 {
3295         _marker->set_position (_marker->tempo().frame());
3296         if (moved) {
3297                 TempoMap& map (_editor->session()->tempo_map());
3298                 /* we removed it before, so add it back now */
3299                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3300                 // delete the dummy marker we used for visual representation while moving.
3301                 // a new visual marker will show up automatically.
3302                 delete _marker;
3303         }
3304 }
3305
3306 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3307         : Drag (e, &c.track_canvas_item(), false)
3308         , _cursor (c)
3309         , _stop (s)
3310 {
3311         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3312 }
3313
3314 /** Do all the things we do when dragging the playhead to make it look as though
3315  *  we have located, without actually doing the locate (because that would cause
3316  *  the diskstream buffers to be refilled, which is too slow).
3317  */
3318 void
3319 CursorDrag::fake_locate (framepos_t t)
3320 {
3321         if (_editor->session () == 0) {
3322                 return;
3323         }
3324
3325         _editor->playhead_cursor->set_position (t);
3326
3327         Session* s = _editor->session ();
3328         if (s->timecode_transmission_suspended ()) {
3329                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3330                 /* This is asynchronous so it will be sent "now"
3331                  */
3332                 s->send_mmc_locate (f);
3333                 /* These are synchronous and will be sent during the next
3334                    process cycle
3335                 */
3336                 s->queue_full_time_code ();
3337                 s->queue_song_position_pointer ();
3338         }
3339
3340         show_verbose_cursor_time (t);
3341         _editor->UpdateAllTransportClocks (t);
3342 }
3343
3344 void
3345 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3346 {
3347         Drag::start_grab (event, c);
3348         setup_snap_delta (_editor->playhead_cursor->current_frame ());
3349
3350         _grab_zoom = _editor->samples_per_pixel;
3351
3352         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3353
3354         _editor->snap_to_with_modifier (where, event);
3355
3356         _editor->_dragging_playhead = true;
3357
3358         Session* s = _editor->session ();
3359
3360         /* grab the track canvas item as well */
3361
3362         _cursor.track_canvas_item().grab();
3363
3364         if (s) {
3365                 if (_was_rolling && _stop) {
3366                         s->request_stop ();
3367                 }
3368
3369                 if (s->is_auditioning()) {
3370                         s->cancel_audition ();
3371                 }
3372
3373
3374                 if (AudioEngine::instance()->connected()) {
3375
3376                         /* do this only if we're the engine is connected
3377                          * because otherwise this request will never be
3378                          * serviced and we'll busy wait forever. likewise,
3379                          * notice if we are disconnected while waiting for the
3380                          * request to be serviced.
3381                          */
3382
3383                         s->request_suspend_timecode_transmission ();
3384                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3385                                 /* twiddle our thumbs */
3386                         }
3387                 }
3388         }
3389
3390         fake_locate (where - snap_delta (event->button.state));
3391 }
3392
3393 void
3394 CursorDrag::motion (GdkEvent* event, bool)
3395 {
3396         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3397         _editor->snap_to_with_modifier (where, event);
3398         if (where != last_pointer_frame()) {
3399                 fake_locate (where - snap_delta (event->button.state));
3400         }
3401 }
3402
3403 void
3404 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3405 {
3406         _editor->_dragging_playhead = false;
3407
3408         _cursor.track_canvas_item().ungrab();
3409
3410         if (!movement_occurred && _stop) {
3411                 return;
3412         }
3413
3414         motion (event, false);
3415
3416         Session* s = _editor->session ();
3417         if (s) {
3418                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3419                 _editor->_pending_locate_request = true;
3420                 s->request_resume_timecode_transmission ();
3421         }
3422 }
3423
3424 void
3425 CursorDrag::aborted (bool)
3426 {
3427         _cursor.track_canvas_item().ungrab();
3428
3429         if (_editor->_dragging_playhead) {
3430                 _editor->session()->request_resume_timecode_transmission ();
3431                 _editor->_dragging_playhead = false;
3432         }
3433
3434         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3435 }
3436
3437 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3438         : RegionDrag (e, i, p, v)
3439 {
3440         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3441 }
3442
3443 void
3444 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3445 {
3446         Drag::start_grab (event, cursor);
3447
3448         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3449         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3450         setup_snap_delta (r->position ());
3451
3452         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3453 }
3454
3455 void
3456 FadeInDrag::setup_pointer_frame_offset ()
3457 {
3458         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3459         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3460         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3461 }
3462
3463 void
3464 FadeInDrag::motion (GdkEvent* event, bool)
3465 {
3466         framecnt_t fade_length;
3467
3468         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3469         _editor->snap_to_with_modifier (pos, event);
3470         pos -= snap_delta (event->button.state);
3471
3472         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3473
3474         if (pos < (region->position() + 64)) {
3475                 fade_length = 64; // this should be a minimum defined somewhere
3476         } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3477                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3478         } else {
3479                 fade_length = pos - region->position();
3480         }
3481
3482         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3483
3484                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3485
3486                 if (!tmp) {
3487                         continue;
3488                 }
3489
3490                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3491         }
3492
3493         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3494 }
3495
3496 void
3497 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3498 {
3499         if (!movement_occurred) {
3500                 return;
3501         }
3502
3503         framecnt_t fade_length;
3504         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3505         _editor->snap_to_with_modifier (pos, event);
3506         pos -= snap_delta (event->button.state);
3507
3508         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3509
3510         if (pos < (region->position() + 64)) {
3511                 fade_length = 64; // this should be a minimum defined somewhere
3512         } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3513                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3514         } else {
3515                 fade_length = pos - region->position();
3516         }
3517
3518         bool in_command = false;
3519
3520         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3521
3522                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3523
3524                 if (!tmp) {
3525                         continue;
3526                 }
3527
3528                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3529                 XMLNode &before = alist->get_state();
3530
3531                 tmp->audio_region()->set_fade_in_length (fade_length);
3532                 tmp->audio_region()->set_fade_in_active (true);
3533
3534                 if (!in_command) {
3535                         _editor->begin_reversible_command (_("change fade in length"));
3536                         in_command = true;
3537                 }
3538                 XMLNode &after = alist->get_state();
3539                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3540         }
3541
3542         if (in_command) {
3543                 _editor->commit_reversible_command ();
3544         }
3545 }
3546
3547 void
3548 FadeInDrag::aborted (bool)
3549 {
3550         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3551                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3552
3553                 if (!tmp) {
3554                         continue;
3555                 }
3556
3557                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3558         }
3559 }
3560
3561 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3562         : RegionDrag (e, i, p, v)
3563 {
3564         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3565 }
3566
3567 void
3568 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3569 {
3570         Drag::start_grab (event, cursor);
3571
3572         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3573         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3574         setup_snap_delta (r->last_frame ());
3575
3576         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3577 }
3578
3579 void
3580 FadeOutDrag::setup_pointer_frame_offset ()
3581 {
3582         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3583         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3584         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3585 }
3586
3587 void
3588 FadeOutDrag::motion (GdkEvent* event, bool)
3589 {
3590         framecnt_t fade_length;
3591
3592         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3593         _editor->snap_to_with_modifier (pos, event);
3594         pos -= snap_delta (event->button.state);
3595
3596         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3597
3598         if (pos > (region->last_frame() - 64)) {
3599                 fade_length = 64; // this should really be a minimum fade defined somewhere
3600         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3601                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3602         } else {
3603                 fade_length = region->last_frame() - pos;
3604         }
3605
3606         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3607
3608                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3609
3610                 if (!tmp) {
3611                         continue;
3612                 }
3613
3614                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3615         }
3616
3617         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3618 }
3619
3620 void
3621 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3622 {
3623         if (!movement_occurred) {
3624                 return;
3625         }
3626
3627         framecnt_t fade_length;
3628
3629         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3630         _editor->snap_to_with_modifier (pos, event);
3631         pos -= snap_delta (event->button.state);
3632
3633         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3634
3635         if (pos > (region->last_frame() - 64)) {
3636                 fade_length = 64; // this should really be a minimum fade defined somewhere
3637         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3638                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3639         } else {
3640                 fade_length = region->last_frame() - pos;
3641         }
3642
3643         bool in_command = false;
3644
3645         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3646
3647                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3648
3649                 if (!tmp) {
3650                         continue;
3651                 }
3652
3653                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3654                 XMLNode &before = alist->get_state();
3655
3656                 tmp->audio_region()->set_fade_out_length (fade_length);
3657                 tmp->audio_region()->set_fade_out_active (true);
3658
3659                 if (!in_command) {
3660                         _editor->begin_reversible_command (_("change fade out length"));
3661                         in_command = true;
3662                 }
3663                 XMLNode &after = alist->get_state();
3664                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3665         }
3666
3667         if (in_command) {
3668                 _editor->commit_reversible_command ();
3669         }
3670 }
3671
3672 void
3673 FadeOutDrag::aborted (bool)
3674 {
3675         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3676                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3677
3678                 if (!tmp) {
3679                         continue;
3680                 }
3681
3682                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3683         }
3684 }
3685
3686 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3687         : Drag (e, i)
3688 {
3689         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3690
3691         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3692         assert (_marker);
3693
3694         _points.push_back (ArdourCanvas::Duple (0, 0));
3695         _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3696 }
3697
3698 MarkerDrag::~MarkerDrag ()
3699 {
3700         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3701                 delete i->location;
3702         }
3703 }
3704
3705 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3706 {
3707         location = new Location (*l);
3708         markers.push_back (m);
3709         move_both = false;
3710 }
3711
3712 void
3713 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3714 {
3715         Drag::start_grab (event, cursor);
3716
3717         bool is_start;
3718
3719         Location *location = _editor->find_location_from_marker (_marker, is_start);
3720         _editor->_dragging_edit_point = true;
3721
3722         update_item (location);
3723
3724         // _drag_line->show();
3725         // _line->raise_to_top();
3726
3727         if (is_start) {
3728                 show_verbose_cursor_time (location->start());
3729         } else {
3730                 show_verbose_cursor_time (location->end());
3731         }
3732
3733         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3734
3735         switch (op) {
3736         case Selection::Toggle:
3737                 /* we toggle on the button release */
3738                 break;
3739         case Selection::Set:
3740                 if (!_editor->selection->selected (_marker)) {
3741                         _editor->selection->set (_marker);
3742                 }
3743                 break;
3744         case Selection::Extend:
3745         {
3746                 Locations::LocationList ll;
3747                 list<ArdourMarker*> to_add;
3748                 framepos_t s, e;
3749                 _editor->selection->markers.range (s, e);
3750                 s = min (_marker->position(), s);
3751                 e = max (_marker->position(), e);
3752                 s = min (s, e);
3753                 e = max (s, e);
3754                 if (e < max_framepos) {
3755                         ++e;
3756                 }
3757                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3758                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3759                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3760                         if (lm) {
3761                                 if (lm->start) {
3762                                         to_add.push_back (lm->start);
3763                                 }
3764                                 if (lm->end) {
3765                                         to_add.push_back (lm->end);
3766                                 }
3767                         }
3768                 }
3769                 if (!to_add.empty()) {
3770                         _editor->selection->add (to_add);
3771                 }
3772                 break;
3773         }
3774         case Selection::Add:
3775                 _editor->selection->add (_marker);
3776                 break;
3777         }
3778
3779         /* Set up copies for us to manipulate during the drag
3780          */
3781
3782         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3783
3784                 Location* l = _editor->find_location_from_marker (*i, is_start);
3785
3786                 if (!l) {
3787                         continue;
3788                 }
3789
3790                 if (l->is_mark()) {
3791                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3792                 } else {
3793                         /* range: check that the other end of the range isn't
3794                            already there.
3795                         */
3796                         CopiedLocationInfo::iterator x;
3797                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3798                                 if (*(*x).location == *l) {
3799                                         break;
3800                                 }
3801                         }
3802                         if (x == _copied_locations.end()) {
3803                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3804                         } else {
3805                                 (*x).markers.push_back (*i);
3806                                 (*x).move_both = true;
3807                         }
3808                 }
3809
3810         }
3811 }
3812
3813 void
3814 MarkerDrag::setup_pointer_frame_offset ()
3815 {
3816         bool is_start;
3817         Location *location = _editor->find_location_from_marker (_marker, is_start);
3818         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3819 }
3820
3821 void
3822 MarkerDrag::motion (GdkEvent* event, bool)
3823 {
3824         framecnt_t f_delta = 0;
3825         bool is_start;
3826         bool move_both = false;
3827         Location *real_location;
3828         Location *copy_location = 0;
3829
3830         framepos_t const newframe = adjusted_current_frame (event);
3831         framepos_t next = newframe;
3832
3833         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3834                 move_both = true;
3835         }
3836
3837         CopiedLocationInfo::iterator x;
3838
3839         /* find the marker we're dragging, and compute the delta */
3840
3841         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3842
3843                 copy_location = (*x).location;
3844
3845                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3846
3847                         /* this marker is represented by this
3848                          * CopiedLocationMarkerInfo
3849                          */
3850
3851                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3852                                 /* que pasa ?? */
3853                                 return;
3854                         }
3855
3856                         if (real_location->is_mark()) {
3857                                 f_delta = newframe - copy_location->start();
3858                         } else {
3859
3860
3861                                 switch (_marker->type()) {
3862                                 case ArdourMarker::SessionStart:
3863                                 case ArdourMarker::RangeStart:
3864                                 case ArdourMarker::LoopStart:
3865                                 case ArdourMarker::PunchIn:
3866                                         f_delta = newframe - copy_location->start();
3867                                         break;
3868
3869                                 case ArdourMarker::SessionEnd:
3870                                 case ArdourMarker::RangeEnd:
3871                                 case ArdourMarker::LoopEnd:
3872                                 case ArdourMarker::PunchOut:
3873                                         f_delta = newframe - copy_location->end();
3874                                         break;
3875                                 default:
3876                                         /* what kind of marker is this ? */
3877                                         return;
3878                                 }
3879                         }
3880
3881                         break;
3882                 }
3883         }
3884
3885         if (x == _copied_locations.end()) {
3886                 /* hmm, impossible - we didn't find the dragged marker */
3887                 return;
3888         }
3889
3890         /* now move them all */
3891
3892         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3893
3894                 copy_location = x->location;
3895
3896                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3897                         continue;
3898                 }
3899
3900                 if (real_location->locked()) {
3901                         continue;
3902                 }
3903
3904                 if (copy_location->is_mark()) {
3905
3906                         /* now move it */
3907
3908                         copy_location->set_start (copy_location->start() + f_delta);
3909
3910                 } else {
3911
3912                         framepos_t new_start = copy_location->start() + f_delta;
3913                         framepos_t new_end = copy_location->end() + f_delta;
3914
3915                         if (is_start) { // start-of-range marker
3916
3917                                 if (move_both || (*x).move_both) {
3918                                         copy_location->set_start (new_start);
3919                                         copy_location->set_end (new_end);
3920                                 } else  if (new_start < copy_location->end()) {
3921                                         copy_location->set_start (new_start);
3922                                 } else if (newframe > 0) {
3923                                         _editor->snap_to (next, RoundUpAlways, true);
3924                                         copy_location->set_end (next);
3925                                         copy_location->set_start (newframe);
3926                                 }
3927
3928                         } else { // end marker
3929
3930                                 if (move_both || (*x).move_both) {
3931                                         copy_location->set_end (new_end);
3932                                         copy_location->set_start (new_start);
3933                                 } else if (new_end > copy_location->start()) {
3934                                         copy_location->set_end (new_end);
3935                                 } else if (newframe > 0) {
3936                                         _editor->snap_to (next, RoundDownAlways, true);
3937                                         copy_location->set_start (next);
3938                                         copy_location->set_end (newframe);
3939                                 }
3940                         }
3941                 }
3942
3943                 update_item (copy_location);
3944
3945                 /* now lookup the actual GUI items used to display this
3946                  * location and move them to wherever the copy of the location
3947                  * is now. This means that the logic in ARDOUR::Location is
3948                  * still enforced, even though we are not (yet) modifying
3949                  * the real Location itself.
3950                  */
3951
3952                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3953
3954                 if (lm) {
3955                         lm->set_position (copy_location->start(), copy_location->end());
3956                 }
3957
3958         }
3959
3960         assert (!_copied_locations.empty());
3961
3962         show_verbose_cursor_time (newframe);
3963 }
3964
3965 void
3966 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3967 {
3968         if (!movement_occurred) {
3969
3970                 if (was_double_click()) {
3971                         _editor->rename_marker (_marker);
3972                         return;
3973                 }
3974
3975                 /* just a click, do nothing but finish
3976                    off the selection process
3977                 */
3978
3979                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3980
3981                 switch (op) {
3982                 case Selection::Set:
3983                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3984                                 _editor->selection->set (_marker);
3985                         }
3986                         break;
3987
3988                 case Selection::Toggle:
3989                         /* we toggle on the button release, click only */
3990                         _editor->selection->toggle (_marker);
3991                         break;
3992
3993                 case Selection::Extend:
3994                 case Selection::Add:
3995                         break;
3996                 }
3997
3998                 return;
3999         }
4000
4001         _editor->_dragging_edit_point = false;
4002
4003         XMLNode &before = _editor->session()->locations()->get_state();
4004         bool in_command = false;
4005
4006         MarkerSelection::iterator i;
4007         CopiedLocationInfo::iterator x;
4008         bool is_start;
4009
4010         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4011              x != _copied_locations.end() && i != _editor->selection->markers.end();
4012              ++i, ++x) {
4013
4014                 Location * location = _editor->find_location_from_marker (*i, is_start);
4015
4016                 if (location) {
4017
4018                         if (location->locked()) {
4019                                 continue;
4020                         }
4021                         if (!in_command) {
4022                                 _editor->begin_reversible_command ( _("move marker") );
4023                                 in_command = true;
4024                         }
4025                         if (location->is_mark()) {
4026                                 location->set_start (((*x).location)->start());
4027                         } else {
4028                                 location->set (((*x).location)->start(), ((*x).location)->end());
4029                         }
4030                 }
4031         }
4032
4033         if (in_command) {
4034                 XMLNode &after = _editor->session()->locations()->get_state();
4035                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4036                 _editor->commit_reversible_command ();
4037         }
4038 }
4039
4040 void
4041 MarkerDrag::aborted (bool movement_occured)
4042 {
4043         if (!movement_occured) {
4044                 return;
4045         }
4046
4047         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4048
4049                 /* move all markers to their original location */
4050
4051
4052                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4053
4054                         bool is_start;
4055                         Location * location = _editor->find_location_from_marker (*m, is_start);
4056
4057                         if (location) {
4058                                 (*m)->set_position (is_start ? location->start() : location->end());
4059                         }
4060                 }
4061         }
4062 }
4063
4064 void
4065 MarkerDrag::update_item (Location*)
4066 {
4067         /* noop */
4068 }
4069
4070 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4071         : Drag (e, i),
4072           _cumulative_x_drag (0)
4073         , _cumulative_y_drag (0)
4074         , _pushing (false)
4075 {
4076         if (_zero_gain_fraction < 0.0) {
4077                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4078         }
4079
4080         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4081
4082         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4083         assert (_point);
4084 }
4085
4086
4087 void
4088 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4089 {
4090         Drag::start_grab (event, _editor->cursors()->fader);
4091
4092         // start the grab at the center of the control point so
4093         // the point doesn't 'jump' to the mouse after the first drag
4094         _fixed_grab_x = _point->get_x();
4095         _fixed_grab_y = _point->get_y();
4096
4097         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4098         setup_snap_delta (pos);
4099
4100         float const fraction = 1 - (_point->get_y() / _point->line().height());
4101         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4102
4103         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4104
4105         if (!_point->can_slide ()) {
4106                 _x_constrained = true;
4107         }
4108 }
4109
4110 void
4111 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4112 {
4113         double dx = _drags->current_pointer_x() - last_pointer_x();
4114         double dy = current_pointer_y() - last_pointer_y();
4115
4116         if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4117                 dx *= 0.1;
4118                 dy *= 0.1;
4119         }
4120
4121         /* coordinate in pixels relative to the start of the region (for region-based automation)
4122            or track (for track-based automation) */
4123         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4124         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4125
4126         // calculate zero crossing point. back off by .01 to stay on the
4127         // positive side of zero
4128         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4129
4130         if (_x_constrained) {
4131                 cx = _fixed_grab_x;
4132         }
4133         if (_y_constrained) {
4134                 cy = _fixed_grab_y;
4135         }
4136
4137         _cumulative_x_drag = cx - _fixed_grab_x;
4138         _cumulative_y_drag = cy - _fixed_grab_y;
4139
4140         // make sure we hit zero when passing through
4141         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4142                 cy = zero_gain_y;
4143         }
4144
4145         cx = max (0.0, cx);
4146         cy = max (0.0, cy);
4147         cy = min ((double) _point->line().height(), cy);
4148
4149         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4150
4151         if (!_x_constrained) {
4152                 _editor->snap_to_with_modifier (cx_frames, event);
4153         }
4154
4155         cx_frames -= snap_delta (event->button.state);
4156         cx_frames = min (cx_frames, _point->line().maximum_time());
4157
4158         float const fraction = 1.0 - (cy / _point->line().height());
4159
4160         if (first_motion) {
4161                 _editor->begin_reversible_command (_("automation event move"));
4162                 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4163         }
4164
4165         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4166
4167         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4168 }
4169
4170 void
4171 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4172 {
4173         if (!movement_occurred) {
4174
4175                 /* just a click */
4176                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4177                         _editor->reset_point_selection ();
4178                 }
4179
4180         } else {
4181                 motion (event, false);
4182                 _point->line().end_drag (_pushing, _final_index);
4183                 _editor->commit_reversible_command ();
4184         }
4185 }
4186
4187 void
4188 ControlPointDrag::aborted (bool)
4189 {
4190         _point->line().reset ();
4191 }
4192
4193 bool
4194 ControlPointDrag::active (Editing::MouseMode m)
4195 {
4196         if (m == Editing::MouseDraw) {
4197                 /* always active in mouse draw */
4198                 return true;
4199         }
4200
4201         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4202         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4203 }
4204
4205 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4206         : Drag (e, i)
4207         , _line (0)
4208         , _cumulative_y_drag (0)
4209         , _before (0)
4210         , _after (0)
4211 {
4212         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4213 }
4214
4215 void
4216 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4217 {
4218         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4219         assert (_line);
4220
4221         _item = &_line->grab_item ();
4222
4223         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4224            origin, and ditto for y.
4225         */
4226
4227         double cx = event->button.x;
4228         double cy = event->button.y;
4229
4230         _line->parent_group().canvas_to_item (cx, cy);
4231
4232         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4233
4234         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4235                 /* no adjacent points */
4236                 return;
4237         }
4238
4239         Drag::start_grab (event, _editor->cursors()->fader);
4240
4241         /* store grab start in parent frame */
4242
4243         _fixed_grab_x = cx;
4244         _fixed_grab_y = cy;
4245
4246         double fraction = 1.0 - (cy / _line->height());
4247
4248         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4249 }
4250
4251 void
4252 LineDrag::motion (GdkEvent* event, bool first_move)
4253 {
4254         double dy = current_pointer_y() - last_pointer_y();
4255
4256         if (event->button.state & ArdourKeyboard::fine_adjust_modifier ()) {
4257                 dy *= 0.1;
4258         }
4259
4260         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4261
4262         _cumulative_y_drag = cy - _fixed_grab_y;
4263
4264         cy = max (0.0, cy);
4265         cy = min ((double) _line->height(), cy);
4266
4267         double const fraction = 1.0 - (cy / _line->height());
4268         uint32_t ignored;
4269
4270         if (first_move) {
4271                 _editor->begin_reversible_command (_("automation range move"));
4272                 _line->start_drag_line (_before, _after, fraction);
4273         }
4274
4275         /* we are ignoring x position for this drag, so we can just pass in anything */
4276         _line->drag_motion (0, fraction, true, false, ignored);
4277
4278         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4279 }
4280
4281 void
4282 LineDrag::finished (GdkEvent* event, bool movement_occured)
4283 {
4284         if (movement_occured) {
4285                 motion (event, false);
4286                 _line->end_drag (false, 0);
4287                 _editor->commit_reversible_command ();
4288         } else {
4289                 /* add a new control point on the line */
4290
4291                 AutomationTimeAxisView* atv;
4292
4293                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4294                         framepos_t where = _editor->canvas_event_sample (event, 0, 0);
4295
4296                         atv->add_automation_event (event, where, event->button.y, false);
4297                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4298                         AudioRegionView* arv;
4299
4300                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4301                                 arv->add_gain_point_event (arv->get_canvas_group (), event, false);
4302                         }
4303                 }
4304         }
4305 }
4306
4307 void
4308 LineDrag::aborted (bool)
4309 {
4310         _line->reset ();
4311 }
4312
4313 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4314         : Drag (e, i),
4315           _line (0),
4316           _cumulative_x_drag (0)
4317 {
4318         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4319 }
4320
4321 void
4322 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4323 {
4324         Drag::start_grab (event);
4325
4326         _line = reinterpret_cast<Line*> (_item);
4327         assert (_line);
4328
4329         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4330
4331         double cx = event->button.x;
4332         double cy = event->button.y;
4333
4334         _item->parent()->canvas_to_item (cx, cy);
4335
4336         /* store grab start in parent frame */
4337         _region_view_grab_x = cx;
4338
4339         _before = *(float*) _item->get_data ("position");
4340
4341         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4342
4343         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4344 }
4345
4346 void
4347 FeatureLineDrag::motion (GdkEvent*, bool)
4348 {
4349         double dx = _drags->current_pointer_x() - last_pointer_x();
4350
4351         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4352
4353         _cumulative_x_drag += dx;
4354
4355         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4356
4357         if (cx > _max_x){
4358                 cx = _max_x;
4359         }
4360         else if(cx < 0){
4361                 cx = 0;
4362         }
4363
4364         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4365         assert (bbox);
4366         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4367
4368         float *pos = new float;
4369         *pos = cx;
4370
4371         _line->set_data ("position", pos);
4372
4373         _before = cx;
4374 }
4375
4376 void
4377 FeatureLineDrag::finished (GdkEvent*, bool)
4378 {
4379         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4380         _arv->update_transient(_before, _before);
4381 }
4382
4383 void
4384 FeatureLineDrag::aborted (bool)
4385 {
4386         //_line->reset ();
4387 }
4388
4389 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4390         : Drag (e, i)
4391         , _vertical_only (false)
4392 {
4393         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4394 }
4395
4396 void
4397 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4398 {
4399         Drag::start_grab (event);
4400         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4401 }
4402
4403 void
4404 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4405 {
4406         framepos_t start;
4407         framepos_t end;
4408         double y1;
4409         double y2;
4410
4411         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4412
4413         framepos_t grab = grab_frame ();
4414         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4415                 _editor->snap_to_with_modifier (grab, event);
4416         } else {
4417                 grab = raw_grab_frame ();
4418         }
4419
4420         /* base start and end on initial click position */
4421
4422         if (pf < grab) {
4423                 start = pf;
4424                 end = grab;
4425         } else {
4426                 end = pf;
4427                 start = grab;
4428         }
4429
4430         if (current_pointer_y() < grab_y()) {
4431                 y1 = current_pointer_y();
4432                 y2 = grab_y();
4433         } else {
4434                 y2 = current_pointer_y();
4435                 y1 = grab_y();
4436         }
4437
4438         if (start != end || y1 != y2) {
4439
4440                 double x1 = _editor->sample_to_pixel (start);
4441                 double x2 = _editor->sample_to_pixel (end);
4442                 const double min_dimension = 2.0;
4443
4444                 if (_vertical_only) {
4445                         /* fixed 10 pixel width */
4446                         x2 = x1 + 10;
4447                 } else {
4448                         if (x2 < x1) {
4449                                 x2 = min (x1 - min_dimension, x2);
4450                         } else {
4451                                 x2 = max (x1 + min_dimension, x2);
4452                         }
4453                 }
4454
4455                 if (y2 < y1) {
4456                         y2 = min (y1 - min_dimension, y2);
4457                 } else {
4458                         y2 = max (y1 + min_dimension, y2);
4459                 }
4460
4461                 /* translate rect into item space and set */
4462
4463                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4464
4465                 /* this drag is a _trackview_only == true drag, so the y1 and
4466                  * y2 (computed using current_pointer_y() and grab_y()) will be
4467                  * relative to the top of the trackview group). The
4468                  * rubberband rect has the same parent/scroll offset as the
4469                  * the trackview group, so we can use the "r" rect directly
4470                  * to set the shape of the rubberband.
4471                  */
4472
4473                 _editor->rubberband_rect->set (r);
4474                 _editor->rubberband_rect->show();
4475                 _editor->rubberband_rect->raise_to_top();
4476
4477                 show_verbose_cursor_time (pf);
4478
4479                 do_select_things (event, true);
4480         }
4481 }
4482
4483 void
4484 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4485 {
4486         framepos_t x1;
4487         framepos_t x2;
4488         framepos_t grab = grab_frame ();
4489         framepos_t lpf = last_pointer_frame ();
4490
4491         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4492                 grab = raw_grab_frame ();
4493                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4494         }
4495
4496         if (grab < lpf) {
4497                 x1 = grab;
4498                 x2 = lpf;
4499         } else {
4500                 x2 = grab;
4501                 x1 = lpf;
4502         }
4503
4504         double y1;
4505         double y2;
4506
4507         if (current_pointer_y() < grab_y()) {
4508                 y1 = current_pointer_y();
4509                 y2 = grab_y();
4510         } else {
4511                 y2 = current_pointer_y();
4512                 y1 = grab_y();
4513         }
4514
4515         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4516 }
4517
4518 void
4519 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4520 {
4521         if (movement_occurred) {
4522
4523                 motion (event, false);
4524                 do_select_things (event, false);
4525
4526         } else {
4527
4528                 /* just a click */
4529
4530                 bool do_deselect = true;
4531                 MidiTimeAxisView* mtv;
4532
4533                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4534                         /* MIDI track */
4535                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4536                                 /* nothing selected */
4537                                 add_midi_region (mtv);
4538                                 do_deselect = false;
4539                         }
4540                 }
4541
4542                 /* do not deselect if Primary or Tertiary (toggle-select or
4543                  * extend-select are pressed.
4544                  */
4545
4546                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4547                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4548                     do_deselect) {
4549                         deselect_things ();
4550                 }
4551
4552         }
4553
4554         _editor->rubberband_rect->hide();
4555 }
4556
4557 void
4558 RubberbandSelectDrag::aborted (bool)
4559 {
4560         _editor->rubberband_rect->hide ();
4561 }
4562
4563 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4564         : RegionDrag (e, i, p, v)
4565 {
4566         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4567 }
4568
4569 void
4570 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4571 {
4572         Drag::start_grab (event, cursor);
4573
4574         _editor->get_selection().add (_primary);
4575
4576         framepos_t where = _primary->region()->position();
4577         setup_snap_delta (where);
4578
4579         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4580 }
4581
4582 void
4583 TimeFXDrag::motion (GdkEvent* event, bool)
4584 {
4585         RegionView* rv = _primary;
4586         StreamView* cv = rv->get_time_axis_view().view ();
4587
4588         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4589         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4590         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4591         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4592         _editor->snap_to_with_modifier (pf, event);
4593         pf -= snap_delta (event->button.state);
4594
4595         if (pf > rv->region()->position()) {
4596                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4597         }
4598
4599         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4600 }
4601
4602 void
4603 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4604 {
4605         _primary->get_time_axis_view().hide_timestretch ();
4606
4607         if (!movement_occurred) {
4608                 return;
4609         }
4610
4611         if (last_pointer_frame() < _primary->region()->position()) {
4612                 /* backwards drag of the left edge - not usable */
4613                 return;
4614         }
4615
4616         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4617
4618         float percentage = (double) newlen / (double) _primary->region()->length();
4619
4620 #ifndef USE_RUBBERBAND
4621         // Soundtouch uses percentage / 100 instead of normal (/ 1)
4622         if (_primary->region()->data_type() == DataType::AUDIO) {
4623                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4624         }
4625 #endif
4626
4627         if (!_editor->get_selection().regions.empty()) {
4628                 /* primary will already be included in the selection, and edit
4629                    group shared editing will propagate selection across
4630                    equivalent regions, so just use the current region
4631                    selection.
4632                 */
4633
4634                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4635                         error << _("An error occurred while executing time stretch operation") << endmsg;
4636                 }
4637         }
4638 }
4639
4640 void
4641 TimeFXDrag::aborted (bool)
4642 {
4643         _primary->get_time_axis_view().hide_timestretch ();
4644 }
4645
4646 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4647         : Drag (e, i)
4648 {
4649         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4650 }
4651
4652 void
4653 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4654 {
4655         Drag::start_grab (event);
4656 }
4657
4658 void
4659 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4660 {
4661         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4662 }
4663
4664 void
4665 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4666 {
4667         if (movement_occurred && _editor->session()) {
4668                 /* make sure we stop */
4669                 _editor->session()->request_transport_speed (0.0);
4670         }
4671 }
4672
4673 void
4674 ScrubDrag::aborted (bool)
4675 {
4676         /* XXX: TODO */
4677 }
4678
4679 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4680         : Drag (e, i)
4681         , _operation (o)
4682         , _add (false)
4683         , _time_selection_at_start (!_editor->get_selection().time.empty())
4684 {
4685         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4686
4687         if (_time_selection_at_start) {
4688                 start_at_start = _editor->get_selection().time.start();
4689                 end_at_start = _editor->get_selection().time.end_frame();
4690         }
4691 }
4692
4693 void
4694 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4695 {
4696         if (_editor->session() == 0) {
4697                 return;
4698         }
4699
4700         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4701
4702         switch (_operation) {
4703         case CreateSelection:
4704                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4705                         _add = true;
4706                 } else {
4707                         _add = false;
4708                 }
4709                 cursor = _editor->cursors()->selector;
4710                 Drag::start_grab (event, cursor);
4711                 break;
4712
4713         case SelectionStartTrim:
4714                 if (_editor->clicked_axisview) {
4715                         _editor->clicked_axisview->order_selection_trims (_item, true);
4716                 }
4717                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4718                 break;
4719
4720         case SelectionEndTrim:
4721                 if (_editor->clicked_axisview) {
4722                         _editor->clicked_axisview->order_selection_trims (_item, false);
4723                 }
4724                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4725                 break;
4726
4727         case SelectionMove:
4728                 Drag::start_grab (event, cursor);
4729                 break;
4730
4731         case SelectionExtend:
4732                 Drag::start_grab (event, cursor);
4733                 break;
4734         }
4735
4736         if (_operation == SelectionMove) {
4737                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4738         } else {
4739                 show_verbose_cursor_time (adjusted_current_frame (event));
4740         }
4741 }
4742
4743 void
4744 SelectionDrag::setup_pointer_frame_offset ()
4745 {
4746         switch (_operation) {
4747         case CreateSelection:
4748                 _pointer_frame_offset = 0;
4749                 break;
4750
4751         case SelectionStartTrim:
4752         case SelectionMove:
4753                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4754                 break;
4755
4756         case SelectionEndTrim:
4757                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4758                 break;
4759
4760         case SelectionExtend:
4761                 break;
4762         }
4763 }
4764
4765 void
4766 SelectionDrag::motion (GdkEvent* event, bool first_move)
4767 {
4768         framepos_t start = 0;
4769         framepos_t end = 0;
4770         framecnt_t length = 0;
4771         framecnt_t distance = 0;
4772
4773         framepos_t const pending_position = adjusted_current_frame (event);
4774
4775         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4776                 return;
4777         }
4778
4779         switch (_operation) {
4780         case CreateSelection:
4781         {
4782                 framepos_t grab = grab_frame ();
4783
4784                 if (first_move) {
4785                         grab = adjusted_current_frame (event, false);
4786                         if (grab < pending_position) {
4787                                 _editor->snap_to (grab, RoundDownMaybe);
4788                         }  else {
4789                                 _editor->snap_to (grab, RoundUpMaybe);
4790                         }
4791                 }
4792
4793                 if (pending_position < grab) {
4794                         start = pending_position;
4795                         end = grab;
4796                 } else {
4797                         end = pending_position;
4798                         start = grab;
4799                 }
4800
4801                 /* first drag: Either add to the selection
4802                    or create a new selection
4803                 */
4804
4805                 if (first_move) {
4806
4807                         if (_add) {
4808
4809                                 /* adding to the selection */
4810                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4811                                 _editor->clicked_selection = _editor->selection->add (start, end);
4812                                 _add = false;
4813
4814                         } else {
4815
4816                                 /* new selection */
4817
4818                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4819                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4820                                 }
4821
4822                                 _editor->clicked_selection = _editor->selection->set (start, end);
4823                         }
4824                 }
4825
4826                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4827                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4828                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4829                 if (atest) {
4830                         _editor->selection->add (atest);
4831                         break;
4832                 }
4833
4834                 /* select all tracks within the rectangle that we've marked out so far */
4835                 TrackViewList new_selection;
4836                 TrackViewList& all_tracks (_editor->track_views);
4837
4838                 ArdourCanvas::Coord const top = grab_y();
4839                 ArdourCanvas::Coord const bottom = current_pointer_y();
4840
4841                 if (top >= 0 && bottom >= 0) {
4842
4843                         //first, find the tracks that are covered in the y range selection
4844                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4845                                 if ((*i)->covered_by_y_range (top, bottom)) {
4846                                         new_selection.push_back (*i);
4847                                 }
4848                         }
4849
4850                         //now find any tracks that are GROUPED with the tracks we selected
4851                         TrackViewList grouped_add = new_selection;
4852                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4853                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4854                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4855                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4856                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4857                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4858                                                         grouped_add.push_back (*j);
4859                                         }
4860                                 }
4861                         }
4862
4863                         //now compare our list with the current selection, and add or remove as necessary
4864                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4865                         TrackViewList tracks_to_add;
4866                         TrackViewList tracks_to_remove;
4867                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4868                                 if ( !_editor->selection->tracks.contains ( *i ) )
4869                                         tracks_to_add.push_back ( *i );
4870                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4871                                 if ( !grouped_add.contains ( *i ) )
4872                                         tracks_to_remove.push_back ( *i );
4873                         _editor->selection->add(tracks_to_add);
4874                         _editor->selection->remove(tracks_to_remove);
4875
4876                 }
4877         }
4878         break;
4879
4880         case SelectionStartTrim:
4881
4882                 end = _editor->selection->time[_editor->clicked_selection].end;
4883
4884                 if (pending_position > end) {
4885                         start = end;
4886                 } else {
4887                         start = pending_position;
4888                 }
4889                 break;
4890
4891         case SelectionEndTrim:
4892
4893                 start = _editor->selection->time[_editor->clicked_selection].start;
4894
4895                 if (pending_position < start) {
4896                         end = start;
4897                 } else {
4898                         end = pending_position;
4899                 }
4900
4901                 break;
4902
4903         case SelectionMove:
4904
4905                 start = _editor->selection->time[_editor->clicked_selection].start;
4906                 end = _editor->selection->time[_editor->clicked_selection].end;
4907
4908                 length = end - start;
4909                 distance = pending_position - start;
4910                 start = pending_position;
4911                 _editor->snap_to (start);
4912
4913                 end = start + length;
4914
4915                 break;
4916
4917         case SelectionExtend:
4918                 break;
4919         }
4920
4921         if (start != end) {
4922                 switch (_operation) {
4923                 case SelectionMove:
4924                         if (_time_selection_at_start) {
4925                                 _editor->selection->move_time (distance);
4926                         }
4927                         break;
4928                 default:
4929                         _editor->selection->replace (_editor->clicked_selection, start, end);
4930                 }
4931         }
4932
4933         if (_operation == SelectionMove) {
4934                 show_verbose_cursor_time(start);
4935         } else {
4936                 show_verbose_cursor_time(pending_position);
4937         }
4938 }
4939
4940 void
4941 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4942 {
4943         Session* s = _editor->session();
4944
4945         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4946         if (movement_occurred) {
4947                 motion (event, false);
4948                 /* XXX this is not object-oriented programming at all. ick */
4949                 if (_editor->selection->time.consolidate()) {
4950                         _editor->selection->TimeChanged ();
4951                 }
4952
4953                 /* XXX what if its a music time selection? */
4954                 if (s) {
4955                         if (s->get_play_range() && s->transport_rolling()) {
4956                                 s->request_play_range (&_editor->selection->time, true);
4957                         } else {
4958                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
4959                                         if (_operation == SelectionEndTrim)
4960                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4961                                         else
4962                                                 s->request_locate (_editor->get_selection().time.start());
4963                                 }
4964                         }
4965
4966                         if (_editor->get_selection().time.length() != 0) {
4967                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
4968                         } else {
4969                                 s->clear_range_selection ();
4970                         }
4971                 }
4972                 
4973         } else {
4974                 /* just a click, no pointer movement.
4975                  */
4976
4977                 if (_operation == SelectionExtend) {
4978                         if (_time_selection_at_start) {
4979                                 framepos_t pos = adjusted_current_frame (event, false);
4980                                 framepos_t start = min (pos, start_at_start);
4981                                 framepos_t end = max (pos, end_at_start);
4982                                 _editor->selection->set (start, end);
4983                         }
4984                 } else {
4985                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4986                                 if (_editor->clicked_selection) {
4987                                         _editor->selection->remove (_editor->clicked_selection);
4988                                 }
4989                         } else {
4990                                 if (!_editor->clicked_selection) {
4991                                         _editor->selection->clear_time();
4992                                 }
4993                         }
4994                 }
4995
4996                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4997                         _editor->selection->set (_editor->clicked_axisview);
4998                 }
4999
5000                 if (s && s->get_play_range () && s->transport_rolling()) {
5001                         s->request_stop (false, false);
5002                 }
5003
5004         }
5005
5006         _editor->stop_canvas_autoscroll ();
5007         _editor->clicked_selection = 0;
5008         _editor->commit_reversible_selection_op ();
5009 }
5010
5011 void
5012 SelectionDrag::aborted (bool)
5013 {
5014         /* XXX: TODO */
5015 }
5016
5017 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5018         : Drag (e, i, false),
5019           _operation (o),
5020           _copy (false)
5021 {
5022         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5023
5024         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5025                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5026                                                                       physical_screen_height (_editor->get_window())));
5027         _drag_rect->hide ();
5028
5029         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5030         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5031 }
5032
5033 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5034 {
5035         /* normal canvas items will be cleaned up when their parent group is deleted. But
5036            this item is created as the child of a long-lived parent group, and so we
5037            need to explicitly delete it.
5038         */
5039         delete _drag_rect;
5040 }
5041
5042 void
5043 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5044 {
5045         if (_editor->session() == 0) {
5046                 return;
5047         }
5048
5049         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5050
5051         if (!_editor->temp_location) {
5052                 _editor->temp_location = new Location (*_editor->session());
5053         }
5054
5055         switch (_operation) {
5056         case CreateSkipMarker:
5057         case CreateRangeMarker:
5058         case CreateTransportMarker:
5059         case CreateCDMarker:
5060
5061                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5062                         _copy = true;
5063                 } else {
5064                         _copy = false;
5065                 }
5066                 cursor = _editor->cursors()->selector;
5067                 break;
5068         }
5069
5070         Drag::start_grab (event, cursor);
5071
5072         show_verbose_cursor_time (adjusted_current_frame (event));
5073 }
5074
5075 void
5076 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5077 {
5078         framepos_t start = 0;
5079         framepos_t end = 0;
5080         ArdourCanvas::Rectangle *crect;
5081
5082         switch (_operation) {
5083         case CreateSkipMarker:
5084                 crect = _editor->range_bar_drag_rect;
5085                 break;
5086         case CreateRangeMarker:
5087                 crect = _editor->range_bar_drag_rect;
5088                 break;
5089         case CreateTransportMarker:
5090                 crect = _editor->transport_bar_drag_rect;
5091                 break;
5092         case CreateCDMarker:
5093                 crect = _editor->cd_marker_bar_drag_rect;
5094                 break;
5095         default:
5096                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5097                 return;
5098                 break;
5099         }
5100
5101         framepos_t const pf = adjusted_current_frame (event);
5102
5103         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5104                 framepos_t grab = grab_frame ();
5105                 _editor->snap_to (grab);
5106
5107                 if (pf < grab_frame()) {
5108                         start = pf;
5109                         end = grab;
5110                 } else {
5111                         end = pf;
5112                         start = grab;
5113                 }
5114
5115                 /* first drag: Either add to the selection
5116                    or create a new selection.
5117                 */
5118
5119                 if (first_move) {
5120
5121                         _editor->temp_location->set (start, end);
5122
5123                         crect->show ();
5124
5125                         update_item (_editor->temp_location);
5126                         _drag_rect->show();
5127                         //_drag_rect->raise_to_top();
5128
5129                 }
5130         }
5131
5132         if (start != end) {
5133                 _editor->temp_location->set (start, end);
5134
5135                 double x1 = _editor->sample_to_pixel (start);
5136                 double x2 = _editor->sample_to_pixel (end);
5137                 crect->set_x0 (x1);
5138                 crect->set_x1 (x2);
5139
5140                 update_item (_editor->temp_location);
5141         }
5142
5143         show_verbose_cursor_time (pf);
5144
5145 }
5146
5147 void
5148 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5149 {
5150         Location * newloc = 0;
5151         string rangename;
5152         int flags;
5153
5154         if (movement_occurred) {
5155                 motion (event, false);
5156                 _drag_rect->hide();
5157
5158                 switch (_operation) {
5159                 case CreateSkipMarker:
5160                 case CreateRangeMarker:
5161                 case CreateCDMarker:
5162                     {
5163                         XMLNode &before = _editor->session()->locations()->get_state();
5164                         if (_operation == CreateSkipMarker) {
5165                                 _editor->begin_reversible_command (_("new skip marker"));
5166                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5167                                 flags = Location::IsRangeMarker | Location::IsSkip;
5168                                 _editor->range_bar_drag_rect->hide();
5169                         } else if (_operation == CreateCDMarker) {
5170                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5171                                 _editor->begin_reversible_command (_("new CD marker"));
5172                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5173                                 _editor->cd_marker_bar_drag_rect->hide();
5174                         } else {
5175                                 _editor->begin_reversible_command (_("new skip marker"));
5176                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5177                                 flags = Location::IsRangeMarker;
5178                                 _editor->range_bar_drag_rect->hide();
5179                         }
5180                         newloc = new Location (
5181                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5182                                 );
5183
5184                         _editor->session()->locations()->add (newloc, true);
5185                         XMLNode &after = _editor->session()->locations()->get_state();
5186                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5187                         _editor->commit_reversible_command ();
5188                         break;
5189                     }
5190
5191                 case CreateTransportMarker:
5192                         // popup menu to pick loop or punch
5193                         _editor->new_transport_marker_context_menu (&event->button, _item);
5194                         break;
5195                 }
5196
5197         } else {
5198
5199                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5200
5201                 if (_operation == CreateTransportMarker) {
5202
5203                         /* didn't drag, so just locate */
5204
5205                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5206
5207                 } else if (_operation == CreateCDMarker) {
5208
5209                         /* didn't drag, but mark is already created so do
5210                          * nothing */
5211
5212                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5213
5214                         framepos_t start;
5215                         framepos_t end;
5216
5217                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5218
5219                         if (end == max_framepos) {
5220                                 end = _editor->session()->current_end_frame ();
5221                         }
5222
5223                         if (start == max_framepos) {
5224                                 start = _editor->session()->current_start_frame ();
5225                         }
5226
5227                         switch (_editor->mouse_mode) {
5228                         case MouseObject:
5229                                 /* find the two markers on either side and then make the selection from it */
5230                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5231                                 break;
5232
5233                         case MouseRange:
5234                                 /* find the two markers on either side of the click and make the range out of it */
5235                                 _editor->selection->set (start, end);
5236                                 break;
5237
5238                         default:
5239                                 break;
5240                         }
5241                 }
5242         }
5243
5244         _editor->stop_canvas_autoscroll ();
5245 }
5246
5247 void
5248 RangeMarkerBarDrag::aborted (bool movement_occured)
5249 {
5250         if (movement_occured) {
5251                 _drag_rect->hide ();
5252         }
5253 }
5254
5255 void
5256 RangeMarkerBarDrag::update_item (Location* location)
5257 {
5258         double const x1 = _editor->sample_to_pixel (location->start());
5259         double const x2 = _editor->sample_to_pixel (location->end());
5260
5261         _drag_rect->set_x0 (x1);
5262         _drag_rect->set_x1 (x2);
5263 }
5264
5265 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5266         : Drag (e, i)
5267         , _cumulative_dx (0)
5268         , _cumulative_dy (0)
5269 {
5270         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5271
5272         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5273         assert (_primary);
5274         _region = &_primary->region_view ();
5275         _note_height = _region->midi_stream_view()->note_height ();
5276 }
5277
5278 void
5279 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5280 {
5281         Drag::start_grab (event);
5282         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5283
5284         if (!(_was_selected = _primary->selected())) {
5285
5286                 /* tertiary-click means extend selection - we'll do that on button release,
5287                    so don't add it here, because otherwise we make it hard to figure
5288                    out the "extend-to" range.
5289                 */
5290
5291                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5292
5293                 if (!extend) {
5294                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5295
5296                         if (add) {
5297                                 _region->note_selected (_primary, true);
5298                         } else {
5299                                 _region->unique_select (_primary);
5300                         }
5301
5302                         _editor->begin_reversible_selection_op(X_("Select Note Press"));
5303                         _editor->commit_reversible_selection_op();
5304                 }
5305         }
5306 }
5307
5308 /** @return Current total drag x change in frames */
5309 frameoffset_t
5310 NoteDrag::total_dx (const guint state) const
5311 {
5312         /* dx in frames */
5313         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5314
5315         /* primary note time */
5316         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5317
5318         /* new time of the primary note in session frames */
5319         frameoffset_t st = n + dx + snap_delta (state);
5320
5321         framepos_t const rp = _region->region()->position ();
5322
5323         /* prevent the note being dragged earlier than the region's position */
5324         st = max (st, rp);
5325
5326         /* possibly snap and return corresponding delta */
5327
5328         bool snap = true;
5329
5330         if (ArdourKeyboard::indicates_snap (state)) {
5331                 if (_editor->snap_mode () != SnapOff) {
5332                         snap = false;
5333                 }
5334         } else {
5335                 if (_editor->snap_mode () == SnapOff) {
5336                         snap = false;
5337                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5338                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5339                                 snap = true;
5340                         }
5341                 }
5342         }
5343
5344         frameoffset_t ret;
5345         if (snap) {
5346                 ret =  _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5347         } else {
5348                 ret = st - n - snap_delta (state);
5349         }
5350         return ret;
5351 }
5352
5353 /** @return Current total drag y change in note number */
5354 int8_t
5355 NoteDrag::total_dy () const
5356 {
5357         MidiStreamView* msv = _region->midi_stream_view ();
5358         double const y = _region->midi_view()->y_position ();
5359         /* new current note */
5360         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5361         /* clamp */
5362         n = max (msv->lowest_note(), n);
5363         n = min (msv->highest_note(), n);
5364         /* and work out delta */
5365         return n - msv->y_to_note (grab_y() - y);
5366 }
5367
5368 void
5369 NoteDrag::motion (GdkEvent * event, bool)
5370 {
5371         /* Total change in x and y since the start of the drag */
5372         frameoffset_t const dx = total_dx (event->button.state);
5373         int8_t const dy = total_dy ();
5374
5375         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5376         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5377         double const tdy = -dy * _note_height - _cumulative_dy;
5378
5379         if (tdx || tdy) {
5380                 _cumulative_dx += tdx;
5381                 _cumulative_dy += tdy;
5382
5383                 int8_t note_delta = total_dy();
5384
5385                 _region->move_selection (tdx, tdy, note_delta);
5386
5387                 /* the new note value may be the same as the old one, but we
5388                  * don't know what that means because the selection may have
5389                  * involved more than one note and we might be doing something
5390                  * odd with them. so show the note value anyway, always.
5391                  */
5392
5393                 char buf[12];
5394                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5395
5396                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5397                           (int) floor ((double)new_note));
5398
5399                 show_verbose_cursor_text (buf);
5400         }
5401 }
5402
5403 void
5404 NoteDrag::finished (GdkEvent* ev, bool moved)
5405 {
5406         if (!moved) {
5407                 /* no motion - select note */
5408
5409                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5410                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5411
5412                         bool changed = false;
5413
5414                         if (_was_selected) {
5415                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5416                                 if (add) {
5417                                         _region->note_deselected (_primary);
5418                                         changed = true;
5419                                 }
5420                         } else {
5421                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5422                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5423
5424                                 if (!extend && !add && _region->selection_size() > 1) {
5425                                         _region->unique_select (_primary);
5426                                         changed = true;
5427                                 } else if (extend) {
5428                                         _region->note_selected (_primary, true, true);
5429                                         changed = true;
5430                                 } else {
5431                                         /* it was added during button press */
5432                                 }
5433                         }
5434
5435                         if (changed) {
5436                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5437                                 _editor->commit_reversible_selection_op();
5438                         }
5439                 }
5440         } else {
5441                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5442         }
5443 }
5444
5445 void
5446 NoteDrag::aborted (bool)
5447 {
5448         /* XXX: TODO */
5449 }
5450
5451 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5452 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5453         : Drag (editor, atv->base_item ())
5454         , _ranges (r)
5455         , _y_origin (atv->y_position())
5456         , _nothing_to_drag (false)
5457 {
5458         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5459         setup (atv->lines ());
5460 }
5461
5462 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5463 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5464         : Drag (editor, rv->get_canvas_group ())
5465         , _ranges (r)
5466         , _y_origin (rv->get_time_axis_view().y_position())
5467         , _nothing_to_drag (false)
5468         , _integral (false)
5469 {
5470         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5471
5472         list<boost::shared_ptr<AutomationLine> > lines;
5473
5474         AudioRegionView*      audio_view;
5475         AutomationRegionView* automation_view;
5476         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5477                 lines.push_back (audio_view->get_gain_line ());
5478         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5479                 lines.push_back (automation_view->line ());
5480                 _integral = true;
5481         } else {
5482                 error << _("Automation range drag created for invalid region type") << endmsg;
5483         }
5484
5485         setup (lines);
5486 }
5487
5488 /** @param lines AutomationLines to drag.
5489  *  @param offset Offset from the session start to the points in the AutomationLines.
5490  */
5491 void
5492 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5493 {
5494         /* find the lines that overlap the ranges being dragged */
5495         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5496         while (i != lines.end ()) {
5497                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5498                 ++j;
5499
5500                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5501
5502                 /* check this range against all the AudioRanges that we are using */
5503                 list<AudioRange>::const_iterator k = _ranges.begin ();
5504                 while (k != _ranges.end()) {
5505                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5506                                 break;
5507                         }
5508                         ++k;
5509                 }
5510
5511                 /* add it to our list if it overlaps at all */
5512                 if (k != _ranges.end()) {
5513                         Line n;
5514                         n.line = *i;
5515                         n.state = 0;
5516                         n.range = r;
5517                         _lines.push_back (n);
5518                 }
5519
5520                 i = j;
5521         }
5522
5523         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5524 }
5525
5526 double
5527 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5528 {
5529         return 1.0 - ((global_y - _y_origin) / line->height());
5530 }
5531
5532 double
5533 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5534 {
5535         const double v = list->eval(x);
5536         return _integral ? rint(v) : v;
5537 }
5538
5539 void
5540 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5541 {
5542         Drag::start_grab (event, cursor);
5543
5544         /* Get line states before we start changing things */
5545         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5546                 i->state = &i->line->get_state ();
5547                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5548         }
5549
5550         if (_ranges.empty()) {
5551
5552                 /* No selected time ranges: drag all points */
5553                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5554                         uint32_t const N = i->line->npoints ();
5555                         for (uint32_t j = 0; j < N; ++j) {
5556                                 i->points.push_back (i->line->nth (j));
5557                         }
5558                 }
5559
5560         } else {
5561
5562                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5563
5564                         framecnt_t const half = (i->start + i->end) / 2;
5565
5566                         /* find the line that this audio range starts in */
5567                         list<Line>::iterator j = _lines.begin();
5568                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5569                                 ++j;
5570                         }
5571
5572                         if (j != _lines.end()) {
5573                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5574
5575                                 /* j is the line that this audio range starts in; fade into it;
5576                                    64 samples length plucked out of thin air.
5577                                 */
5578
5579                                 framepos_t a = i->start + 64;
5580                                 if (a > half) {
5581                                         a = half;
5582                                 }
5583
5584                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5585                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5586
5587                                 the_list->editor_add (p, value (the_list, p), false);
5588                                 the_list->editor_add (q, value (the_list, q), false);
5589                         }
5590
5591                         /* same thing for the end */
5592
5593                         j = _lines.begin();
5594                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
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 out of it;
5602                                    64 samples length plucked out of thin air.
5603                                 */
5604
5605                                 framepos_t b = i->end - 64;
5606                                 if (b < half) {
5607                                         b = half;
5608                                 }
5609
5610                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5611                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5612
5613                                 the_list->editor_add (p, value (the_list, p), false);
5614                                 the_list->editor_add (q, value (the_list, q), false);
5615                         }
5616                 }
5617
5618                 _nothing_to_drag = true;
5619
5620                 /* Find all the points that should be dragged and put them in the relevant
5621                    points lists in the Line structs.
5622                 */
5623
5624                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5625
5626                         uint32_t const N = i->line->npoints ();
5627                         for (uint32_t j = 0; j < N; ++j) {
5628
5629                                 /* here's a control point on this line */
5630                                 ControlPoint* p = i->line->nth (j);
5631                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5632
5633                                 /* see if it's inside a range */
5634                                 list<AudioRange>::const_iterator k = _ranges.begin ();
5635                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5636                                         ++k;
5637                                 }
5638
5639                                 if (k != _ranges.end()) {
5640                                         /* dragging this point */
5641                                         _nothing_to_drag = false;
5642                                         i->points.push_back (p);
5643                                 }
5644                         }
5645                 }
5646         }
5647
5648         if (_nothing_to_drag) {
5649                 return;
5650         }
5651 }
5652
5653 void
5654 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5655 {
5656         if (_nothing_to_drag) {
5657                 return;
5658         }
5659
5660         if (first_move) {
5661                 _editor->begin_reversible_command (_("automation range move"));
5662                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5663                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5664                 }
5665         }
5666
5667         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5668                 float const f = y_fraction (l->line, current_pointer_y());
5669                 /* we are ignoring x position for this drag, so we can just pass in anything */
5670                 uint32_t ignored;
5671                 l->line->drag_motion (0, f, true, false, ignored);
5672                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5673         }
5674 }
5675
5676 void
5677 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5678 {
5679         if (_nothing_to_drag || !motion_occurred) {
5680                 return;
5681         }
5682
5683         motion (event, false);
5684         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5685                 i->line->end_drag (false, 0);
5686         }
5687
5688         _editor->commit_reversible_command ();
5689 }
5690
5691 void
5692 AutomationRangeDrag::aborted (bool)
5693 {
5694         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5695                 i->line->reset ();
5696         }
5697 }
5698
5699 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5700         : view (v)
5701         , initial_time_axis_view (itav)
5702 {
5703         /* note that time_axis_view may be null if the regionview was created
5704          * as part of a copy operation.
5705          */
5706         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5707         layer = v->region()->layer ();
5708         initial_y = v->get_canvas_group()->position().y;
5709         initial_playlist = v->region()->playlist ();
5710         initial_position = v->region()->position ();
5711         initial_end = v->region()->position () + v->region()->length ();
5712 }
5713
5714 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5715         : Drag (e, i->canvas_item ())
5716         , _region_view (r)
5717         , _patch_change (i)
5718         , _cumulative_dx (0)
5719 {
5720         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5721                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5722                                                    grab_frame()));
5723 }
5724
5725 void
5726 PatchChangeDrag::motion (GdkEvent* ev, bool)
5727 {
5728         framepos_t f = adjusted_current_frame (ev);
5729         boost::shared_ptr<Region> r = _region_view->region ();
5730         f = max (f, r->position ());
5731         f = min (f, r->last_frame ());
5732
5733         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5734         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5735         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5736         _cumulative_dx = dxu;
5737 }
5738
5739 void
5740 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5741 {
5742         if (!movement_occurred) {
5743                 return;
5744         }
5745
5746         boost::shared_ptr<Region> r (_region_view->region ());
5747         framepos_t f = adjusted_current_frame (ev);
5748         f = max (f, r->position ());
5749         f = min (f, r->last_frame ());
5750
5751         _region_view->move_patch_change (
5752                 *_patch_change,
5753                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5754                 );
5755 }
5756
5757 void
5758 PatchChangeDrag::aborted (bool)
5759 {
5760         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5761 }
5762
5763 void
5764 PatchChangeDrag::setup_pointer_frame_offset ()
5765 {
5766         boost::shared_ptr<Region> region = _region_view->region ();
5767         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5768 }
5769
5770 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5771         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5772         , _region_view (rv)
5773 {
5774
5775 }
5776
5777 void
5778 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5779 {
5780         _region_view->update_drag_selection (
5781                 x1, x2, y1, y2,
5782                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5783 }
5784
5785 void
5786 MidiRubberbandSelectDrag::deselect_things ()
5787 {
5788         /* XXX */
5789 }
5790
5791 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5792         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5793         , _region_view (rv)
5794 {
5795         _vertical_only = true;
5796 }
5797
5798 void
5799 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5800 {
5801         double const y = _region_view->midi_view()->y_position ();
5802
5803         y1 = max (0.0, y1 - y);
5804         y2 = max (0.0, y2 - y);
5805
5806         _region_view->update_vertical_drag_selection (
5807                 y1,
5808                 y2,
5809                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5810                 );
5811 }
5812
5813 void
5814 MidiVerticalSelectDrag::deselect_things ()
5815 {
5816         /* XXX */
5817 }
5818
5819 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5820         : RubberbandSelectDrag (e, i)
5821 {
5822
5823 }
5824
5825 void
5826 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5827 {
5828         if (drag_in_progress) {
5829                 /* We just want to select things at the end of the drag, not during it */
5830                 return;
5831         }
5832
5833         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5834
5835         _editor->begin_reversible_selection_op (X_("rubberband selection"));
5836
5837         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5838
5839         _editor->commit_reversible_selection_op ();
5840 }
5841
5842 void
5843 EditorRubberbandSelectDrag::deselect_things ()
5844 {
5845         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5846
5847         _editor->selection->clear_tracks();
5848         _editor->selection->clear_regions();
5849         _editor->selection->clear_points ();
5850         _editor->selection->clear_lines ();
5851         _editor->selection->clear_midi_notes ();
5852
5853         _editor->commit_reversible_selection_op();
5854 }
5855
5856 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5857         : Drag (e, i)
5858         , _region_view (rv)
5859         , _drag_rect (0)
5860 {
5861         _note[0] = _note[1] = 0;
5862 }
5863
5864 NoteCreateDrag::~NoteCreateDrag ()
5865 {
5866         delete _drag_rect;
5867 }
5868
5869 framecnt_t
5870 NoteCreateDrag::grid_frames (framepos_t t) const
5871 {
5872         bool success;
5873         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5874         if (!success) {
5875                 grid_beats = Evoral::Beats(1);
5876         }
5877
5878         return _region_view->region_beats_to_region_frames (grid_beats);
5879 }
5880
5881 void
5882 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5883 {
5884         Drag::start_grab (event, cursor);
5885
5886         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5887
5888         framepos_t pf = _drags->current_pointer_frame ();
5889         framecnt_t const g = grid_frames (pf);
5890
5891         /* Hack so that we always snap to the note that we are over, instead of snapping
5892            to the next one if we're more than halfway through the one we're over.
5893         */
5894         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5895                 pf -= g / 2;
5896         }
5897
5898         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5899         _note[1] = _note[0];
5900
5901         MidiStreamView* sv = _region_view->midi_stream_view ();
5902         double const x = _editor->sample_to_pixel (_note[0]);
5903         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5904
5905         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5906         _drag_rect->set_outline_all ();
5907         _drag_rect->set_outline_color (0xffffff99);
5908         _drag_rect->set_fill_color (0xffffff66);
5909 }
5910
5911 void
5912 NoteCreateDrag::motion (GdkEvent* event, bool)
5913 {
5914         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5915         double const x0 = _editor->sample_to_pixel (_note[0]);
5916         double const x1 = _editor->sample_to_pixel (_note[1]);
5917         _drag_rect->set_x0 (std::min(x0, x1));
5918         _drag_rect->set_x1 (std::max(x0, x1));
5919 }
5920
5921 void
5922 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5923 {
5924         if (!had_movement) {
5925                 return;
5926         }
5927
5928         framepos_t const start = min (_note[0], _note[1]);
5929         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5930
5931         framecnt_t const g = grid_frames (start);
5932         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5933
5934         if (_editor->snap_mode() == SnapNormal && length < g) {
5935                 length = g;
5936         }
5937
5938         Evoral::Beats length_beats = max (
5939                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5940
5941         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5942 }
5943
5944 double
5945 NoteCreateDrag::y_to_region (double y) const
5946 {
5947         double x = 0;
5948         _region_view->get_canvas_group()->canvas_to_item (x, y);
5949         return y;
5950 }
5951
5952 void
5953 NoteCreateDrag::aborted (bool)
5954 {
5955
5956 }
5957
5958 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5959         : Drag (e, i)
5960         , arv (rv)
5961         , start (start_yn)
5962 {
5963         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5964 }
5965
5966 void
5967 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5968 {
5969         Drag::start_grab (event, cursor);
5970 }
5971
5972 void
5973 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5974 {
5975         double distance;
5976         double new_length;
5977         framecnt_t len;
5978
5979         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5980
5981         if (start) {
5982                 distance = _drags->current_pointer_x() - grab_x();
5983                 len = ar->fade_in()->back()->when;
5984         } else {
5985                 distance = grab_x() - _drags->current_pointer_x();
5986                 len = ar->fade_out()->back()->when;
5987         }
5988
5989         /* how long should it be ? */
5990
5991         new_length = len + _editor->pixel_to_sample (distance);
5992
5993         /* now check with the region that this is legal */
5994
5995         new_length = ar->verify_xfade_bounds (new_length, start);
5996
5997         if (start) {
5998                 arv->reset_fade_in_shape_width (ar, new_length);
5999         } else {
6000                 arv->reset_fade_out_shape_width (ar, new_length);
6001         }
6002 }
6003
6004 void
6005 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6006 {
6007         double distance;
6008         double new_length;
6009         framecnt_t len;
6010
6011         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6012
6013         if (start) {
6014                 distance = _drags->current_pointer_x() - grab_x();
6015                 len = ar->fade_in()->back()->when;
6016         } else {
6017                 distance = grab_x() - _drags->current_pointer_x();
6018                 len = ar->fade_out()->back()->when;
6019         }
6020
6021         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6022
6023         _editor->begin_reversible_command ("xfade trim");
6024         ar->playlist()->clear_owned_changes ();
6025
6026         if (start) {
6027                 ar->set_fade_in_length (new_length);
6028         } else {
6029                 ar->set_fade_out_length (new_length);
6030         }
6031
6032         /* Adjusting the xfade may affect other regions in the playlist, so we need
6033            to get undo Commands from the whole playlist rather than just the
6034            region.
6035         */
6036
6037         vector<Command*> cmds;
6038         ar->playlist()->rdiff (cmds);
6039         _editor->session()->add_commands (cmds);
6040         _editor->commit_reversible_command ();
6041
6042 }
6043
6044 void
6045 CrossfadeEdgeDrag::aborted (bool)
6046 {
6047         if (start) {
6048                 // arv->redraw_start_xfade ();
6049         } else {
6050                 // arv->redraw_end_xfade ();
6051         }
6052 }
6053
6054 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6055         : Drag (e, item, true)
6056         , line (new EditorCursor (*e))
6057 {
6058         line->set_position (pos);
6059         line->show ();
6060 }
6061
6062 RegionCutDrag::~RegionCutDrag ()
6063 {
6064         delete line;
6065 }
6066
6067 void
6068 RegionCutDrag::motion (GdkEvent*, bool)
6069 {
6070         framepos_t where = _drags->current_pointer_frame();
6071         _editor->snap_to (where);
6072
6073         line->set_position (where);
6074 }
6075
6076 void
6077 RegionCutDrag::finished (GdkEvent*, bool)
6078 {
6079         _editor->get_track_canvas()->canvas()->re_enter();
6080
6081         framepos_t pos = _drags->current_pointer_frame();
6082
6083         line->hide ();
6084
6085         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6086
6087         if (rs.empty()) {
6088                 return;
6089         }
6090
6091         _editor->split_regions_at (pos, rs);
6092 }
6093
6094 void
6095 RegionCutDrag::aborted (bool)
6096 {
6097 }