53dca40476309b321d4c4b8e94802369c0fab523
[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         bool need_snap = true;
4116
4117         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4118                 dx *= 0.1;
4119                 dy *= 0.1;
4120                 need_snap = false;
4121         }
4122
4123         /* coordinate in pixels relative to the start of the region (for region-based automation)
4124            or track (for track-based automation) */
4125         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4126         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4127
4128         // calculate zero crossing point. back off by .01 to stay on the
4129         // positive side of zero
4130         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4131
4132         if (_x_constrained) {
4133                 cx = _fixed_grab_x;
4134         }
4135         if (_y_constrained) {
4136                 cy = _fixed_grab_y;
4137         }
4138
4139         _cumulative_x_drag = cx - _fixed_grab_x;
4140         _cumulative_y_drag = cy - _fixed_grab_y;
4141
4142         // make sure we hit zero when passing through
4143         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4144                 cy = zero_gain_y;
4145         }
4146
4147         cx = max (0.0, cx);
4148         cy = max (0.0, cy);
4149         cy = min ((double) _point->line().height(), cy);
4150
4151         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4152
4153         if (!_x_constrained && need_snap) {
4154                 _editor->snap_to_with_modifier (cx_frames, event);
4155         }
4156
4157         cx_frames -= snap_delta (event->button.state);
4158         cx_frames = min (cx_frames, _point->line().maximum_time());
4159
4160         float const fraction = 1.0 - (cy / _point->line().height());
4161
4162         if (first_motion) {
4163                 _editor->begin_reversible_command (_("automation event move"));
4164                 _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
4165         }
4166
4167         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4168
4169         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4170 }
4171
4172 void
4173 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4174 {
4175         if (!movement_occurred) {
4176
4177                 /* just a click */
4178                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4179                         _editor->reset_point_selection ();
4180                 }
4181
4182         } else {
4183                 motion (event, false);
4184                 _point->line().end_drag (_pushing, _final_index);
4185                 _editor->commit_reversible_command ();
4186         }
4187 }
4188
4189 void
4190 ControlPointDrag::aborted (bool)
4191 {
4192         _point->line().reset ();
4193 }
4194
4195 bool
4196 ControlPointDrag::active (Editing::MouseMode m)
4197 {
4198         if (m == Editing::MouseDraw) {
4199                 /* always active in mouse draw */
4200                 return true;
4201         }
4202
4203         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4204         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4205 }
4206
4207 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4208         : Drag (e, i)
4209         , _line (0)
4210         , _cumulative_y_drag (0)
4211         , _before (0)
4212         , _after (0)
4213 {
4214         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4215 }
4216
4217 void
4218 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4219 {
4220         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4221         assert (_line);
4222
4223         _item = &_line->grab_item ();
4224
4225         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4226            origin, and ditto for y.
4227         */
4228
4229         double cx = event->button.x;
4230         double cy = event->button.y;
4231
4232         _line->parent_group().canvas_to_item (cx, cy);
4233
4234         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4235
4236         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4237                 /* no adjacent points */
4238                 return;
4239         }
4240
4241         Drag::start_grab (event, _editor->cursors()->fader);
4242
4243         /* store grab start in parent frame */
4244
4245         _fixed_grab_x = cx;
4246         _fixed_grab_y = cy;
4247
4248         double fraction = 1.0 - (cy / _line->height());
4249
4250         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4251 }
4252
4253 void
4254 LineDrag::motion (GdkEvent* event, bool first_move)
4255 {
4256         double dy = current_pointer_y() - last_pointer_y();
4257
4258         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4259                 dy *= 0.1;
4260         }
4261
4262         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4263
4264         _cumulative_y_drag = cy - _fixed_grab_y;
4265
4266         cy = max (0.0, cy);
4267         cy = min ((double) _line->height(), cy);
4268
4269         double const fraction = 1.0 - (cy / _line->height());
4270         uint32_t ignored;
4271
4272         if (first_move) {
4273                 _editor->begin_reversible_command (_("automation range move"));
4274                 _line->start_drag_line (_before, _after, fraction);
4275         }
4276
4277         /* we are ignoring x position for this drag, so we can just pass in anything */
4278         _line->drag_motion (0, fraction, true, false, ignored);
4279
4280         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4281 }
4282
4283 void
4284 LineDrag::finished (GdkEvent* event, bool movement_occured)
4285 {
4286         if (movement_occured) {
4287                 motion (event, false);
4288                 _line->end_drag (false, 0);
4289                 _editor->commit_reversible_command ();
4290         } else {
4291                 /* add a new control point on the line */
4292
4293                 AutomationTimeAxisView* atv;
4294
4295                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4296                         framepos_t where = _editor->canvas_event_sample (event, 0, 0);
4297
4298                         atv->add_automation_event (event, where, event->button.y, false);
4299                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4300                         AudioRegionView* arv;
4301
4302                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4303                                 arv->add_gain_point_event (arv->get_canvas_group (), event, false);
4304                         }
4305                 }
4306         }
4307 }
4308
4309 void
4310 LineDrag::aborted (bool)
4311 {
4312         _line->reset ();
4313 }
4314
4315 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4316         : Drag (e, i),
4317           _line (0),
4318           _cumulative_x_drag (0)
4319 {
4320         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4321 }
4322
4323 void
4324 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4325 {
4326         Drag::start_grab (event);
4327
4328         _line = reinterpret_cast<Line*> (_item);
4329         assert (_line);
4330
4331         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4332
4333         double cx = event->button.x;
4334         double cy = event->button.y;
4335
4336         _item->parent()->canvas_to_item (cx, cy);
4337
4338         /* store grab start in parent frame */
4339         _region_view_grab_x = cx;
4340
4341         _before = *(float*) _item->get_data ("position");
4342
4343         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4344
4345         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4346 }
4347
4348 void
4349 FeatureLineDrag::motion (GdkEvent*, bool)
4350 {
4351         double dx = _drags->current_pointer_x() - last_pointer_x();
4352
4353         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4354
4355         _cumulative_x_drag += dx;
4356
4357         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4358
4359         if (cx > _max_x){
4360                 cx = _max_x;
4361         }
4362         else if(cx < 0){
4363                 cx = 0;
4364         }
4365
4366         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4367         assert (bbox);
4368         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4369
4370         float *pos = new float;
4371         *pos = cx;
4372
4373         _line->set_data ("position", pos);
4374
4375         _before = cx;
4376 }
4377
4378 void
4379 FeatureLineDrag::finished (GdkEvent*, bool)
4380 {
4381         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4382         _arv->update_transient(_before, _before);
4383 }
4384
4385 void
4386 FeatureLineDrag::aborted (bool)
4387 {
4388         //_line->reset ();
4389 }
4390
4391 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4392         : Drag (e, i)
4393         , _vertical_only (false)
4394 {
4395         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4396 }
4397
4398 void
4399 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4400 {
4401         Drag::start_grab (event);
4402         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4403 }
4404
4405 void
4406 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4407 {
4408         framepos_t start;
4409         framepos_t end;
4410         double y1;
4411         double y2;
4412
4413         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4414
4415         framepos_t grab = grab_frame ();
4416         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4417                 _editor->snap_to_with_modifier (grab, event);
4418         } else {
4419                 grab = raw_grab_frame ();
4420         }
4421
4422         /* base start and end on initial click position */
4423
4424         if (pf < grab) {
4425                 start = pf;
4426                 end = grab;
4427         } else {
4428                 end = pf;
4429                 start = grab;
4430         }
4431
4432         if (current_pointer_y() < grab_y()) {
4433                 y1 = current_pointer_y();
4434                 y2 = grab_y();
4435         } else {
4436                 y2 = current_pointer_y();
4437                 y1 = grab_y();
4438         }
4439
4440         if (start != end || y1 != y2) {
4441
4442                 double x1 = _editor->sample_to_pixel (start);
4443                 double x2 = _editor->sample_to_pixel (end);
4444                 const double min_dimension = 2.0;
4445
4446                 if (_vertical_only) {
4447                         /* fixed 10 pixel width */
4448                         x2 = x1 + 10;
4449                 } else {
4450                         if (x2 < x1) {
4451                                 x2 = min (x1 - min_dimension, x2);
4452                         } else {
4453                                 x2 = max (x1 + min_dimension, x2);
4454                         }
4455                 }
4456
4457                 if (y2 < y1) {
4458                         y2 = min (y1 - min_dimension, y2);
4459                 } else {
4460                         y2 = max (y1 + min_dimension, y2);
4461                 }
4462
4463                 /* translate rect into item space and set */
4464
4465                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4466
4467                 /* this drag is a _trackview_only == true drag, so the y1 and
4468                  * y2 (computed using current_pointer_y() and grab_y()) will be
4469                  * relative to the top of the trackview group). The
4470                  * rubberband rect has the same parent/scroll offset as the
4471                  * the trackview group, so we can use the "r" rect directly
4472                  * to set the shape of the rubberband.
4473                  */
4474
4475                 _editor->rubberband_rect->set (r);
4476                 _editor->rubberband_rect->show();
4477                 _editor->rubberband_rect->raise_to_top();
4478
4479                 show_verbose_cursor_time (pf);
4480
4481                 do_select_things (event, true);
4482         }
4483 }
4484
4485 void
4486 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4487 {
4488         framepos_t x1;
4489         framepos_t x2;
4490         framepos_t grab = grab_frame ();
4491         framepos_t lpf = last_pointer_frame ();
4492
4493         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4494                 grab = raw_grab_frame ();
4495                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4496         }
4497
4498         if (grab < lpf) {
4499                 x1 = grab;
4500                 x2 = lpf;
4501         } else {
4502                 x2 = grab;
4503                 x1 = lpf;
4504         }
4505
4506         double y1;
4507         double y2;
4508
4509         if (current_pointer_y() < grab_y()) {
4510                 y1 = current_pointer_y();
4511                 y2 = grab_y();
4512         } else {
4513                 y2 = current_pointer_y();
4514                 y1 = grab_y();
4515         }
4516
4517         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4518 }
4519
4520 void
4521 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4522 {
4523         if (movement_occurred) {
4524
4525                 motion (event, false);
4526                 do_select_things (event, false);
4527
4528         } else {
4529
4530                 /* just a click */
4531
4532                 bool do_deselect = true;
4533                 MidiTimeAxisView* mtv;
4534
4535                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4536                         /* MIDI track */
4537                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4538                                 /* nothing selected */
4539                                 add_midi_region (mtv);
4540                                 do_deselect = false;
4541                         }
4542                 }
4543
4544                 /* do not deselect if Primary or Tertiary (toggle-select or
4545                  * extend-select are pressed.
4546                  */
4547
4548                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4549                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4550                     do_deselect) {
4551                         deselect_things ();
4552                 }
4553
4554         }
4555
4556         _editor->rubberband_rect->hide();
4557 }
4558
4559 void
4560 RubberbandSelectDrag::aborted (bool)
4561 {
4562         _editor->rubberband_rect->hide ();
4563 }
4564
4565 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4566         : RegionDrag (e, i, p, v)
4567 {
4568         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4569 }
4570
4571 void
4572 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4573 {
4574         Drag::start_grab (event, cursor);
4575
4576         _editor->get_selection().add (_primary);
4577
4578         framepos_t where = _primary->region()->position();
4579         setup_snap_delta (where);
4580
4581         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4582 }
4583
4584 void
4585 TimeFXDrag::motion (GdkEvent* event, bool)
4586 {
4587         RegionView* rv = _primary;
4588         StreamView* cv = rv->get_time_axis_view().view ();
4589
4590         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4591         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4592         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4593         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4594         _editor->snap_to_with_modifier (pf, event);
4595         pf -= snap_delta (event->button.state);
4596
4597         if (pf > rv->region()->position()) {
4598                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4599         }
4600
4601         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4602 }
4603
4604 void
4605 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4606 {
4607         _primary->get_time_axis_view().hide_timestretch ();
4608
4609         if (!movement_occurred) {
4610                 return;
4611         }
4612
4613         if (last_pointer_frame() < _primary->region()->position()) {
4614                 /* backwards drag of the left edge - not usable */
4615                 return;
4616         }
4617
4618         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4619
4620         float percentage = (double) newlen / (double) _primary->region()->length();
4621
4622 #ifndef USE_RUBBERBAND
4623         // Soundtouch uses percentage / 100 instead of normal (/ 1)
4624         if (_primary->region()->data_type() == DataType::AUDIO) {
4625                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4626         }
4627 #endif
4628
4629         if (!_editor->get_selection().regions.empty()) {
4630                 /* primary will already be included in the selection, and edit
4631                    group shared editing will propagate selection across
4632                    equivalent regions, so just use the current region
4633                    selection.
4634                 */
4635
4636                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4637                         error << _("An error occurred while executing time stretch operation") << endmsg;
4638                 }
4639         }
4640 }
4641
4642 void
4643 TimeFXDrag::aborted (bool)
4644 {
4645         _primary->get_time_axis_view().hide_timestretch ();
4646 }
4647
4648 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4649         : Drag (e, i)
4650 {
4651         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4652 }
4653
4654 void
4655 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4656 {
4657         Drag::start_grab (event);
4658 }
4659
4660 void
4661 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4662 {
4663         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4664 }
4665
4666 void
4667 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4668 {
4669         if (movement_occurred && _editor->session()) {
4670                 /* make sure we stop */
4671                 _editor->session()->request_transport_speed (0.0);
4672         }
4673 }
4674
4675 void
4676 ScrubDrag::aborted (bool)
4677 {
4678         /* XXX: TODO */
4679 }
4680
4681 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4682         : Drag (e, i)
4683         , _operation (o)
4684         , _add (false)
4685         , _time_selection_at_start (!_editor->get_selection().time.empty())
4686 {
4687         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4688
4689         if (_time_selection_at_start) {
4690                 start_at_start = _editor->get_selection().time.start();
4691                 end_at_start = _editor->get_selection().time.end_frame();
4692         }
4693 }
4694
4695 void
4696 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4697 {
4698         if (_editor->session() == 0) {
4699                 return;
4700         }
4701
4702         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4703
4704         switch (_operation) {
4705         case CreateSelection:
4706                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4707                         _add = true;
4708                 } else {
4709                         _add = false;
4710                 }
4711                 cursor = _editor->cursors()->selector;
4712                 Drag::start_grab (event, cursor);
4713                 break;
4714
4715         case SelectionStartTrim:
4716                 if (_editor->clicked_axisview) {
4717                         _editor->clicked_axisview->order_selection_trims (_item, true);
4718                 }
4719                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4720                 break;
4721
4722         case SelectionEndTrim:
4723                 if (_editor->clicked_axisview) {
4724                         _editor->clicked_axisview->order_selection_trims (_item, false);
4725                 }
4726                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4727                 break;
4728
4729         case SelectionMove:
4730                 Drag::start_grab (event, cursor);
4731                 break;
4732
4733         case SelectionExtend:
4734                 Drag::start_grab (event, cursor);
4735                 break;
4736         }
4737
4738         if (_operation == SelectionMove) {
4739                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4740         } else {
4741                 show_verbose_cursor_time (adjusted_current_frame (event));
4742         }
4743 }
4744
4745 void
4746 SelectionDrag::setup_pointer_frame_offset ()
4747 {
4748         switch (_operation) {
4749         case CreateSelection:
4750                 _pointer_frame_offset = 0;
4751                 break;
4752
4753         case SelectionStartTrim:
4754         case SelectionMove:
4755                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4756                 break;
4757
4758         case SelectionEndTrim:
4759                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4760                 break;
4761
4762         case SelectionExtend:
4763                 break;
4764         }
4765 }
4766
4767 void
4768 SelectionDrag::motion (GdkEvent* event, bool first_move)
4769 {
4770         framepos_t start = 0;
4771         framepos_t end = 0;
4772         framecnt_t length = 0;
4773         framecnt_t distance = 0;
4774
4775         framepos_t const pending_position = adjusted_current_frame (event);
4776
4777         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4778                 return;
4779         }
4780
4781         switch (_operation) {
4782         case CreateSelection:
4783         {
4784                 framepos_t grab = grab_frame ();
4785
4786                 if (first_move) {
4787                         grab = adjusted_current_frame (event, false);
4788                         if (grab < pending_position) {
4789                                 _editor->snap_to (grab, RoundDownMaybe);
4790                         }  else {
4791                                 _editor->snap_to (grab, RoundUpMaybe);
4792                         }
4793                 }
4794
4795                 if (pending_position < grab) {
4796                         start = pending_position;
4797                         end = grab;
4798                 } else {
4799                         end = pending_position;
4800                         start = grab;
4801                 }
4802
4803                 /* first drag: Either add to the selection
4804                    or create a new selection
4805                 */
4806
4807                 if (first_move) {
4808
4809                         if (_add) {
4810
4811                                 /* adding to the selection */
4812                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4813                                 _editor->clicked_selection = _editor->selection->add (start, end);
4814                                 _add = false;
4815
4816                         } else {
4817
4818                                 /* new selection */
4819
4820                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4821                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4822                                 }
4823
4824                                 _editor->clicked_selection = _editor->selection->set (start, end);
4825                         }
4826                 }
4827
4828                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4829                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4830                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4831                 if (atest) {
4832                         _editor->selection->add (atest);
4833                         break;
4834                 }
4835
4836                 /* select all tracks within the rectangle that we've marked out so far */
4837                 TrackViewList new_selection;
4838                 TrackViewList& all_tracks (_editor->track_views);
4839
4840                 ArdourCanvas::Coord const top = grab_y();
4841                 ArdourCanvas::Coord const bottom = current_pointer_y();
4842
4843                 if (top >= 0 && bottom >= 0) {
4844
4845                         //first, find the tracks that are covered in the y range selection
4846                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4847                                 if ((*i)->covered_by_y_range (top, bottom)) {
4848                                         new_selection.push_back (*i);
4849                                 }
4850                         }
4851
4852                         //now find any tracks that are GROUPED with the tracks we selected
4853                         TrackViewList grouped_add = new_selection;
4854                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4855                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4856                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4857                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4858                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4859                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4860                                                         grouped_add.push_back (*j);
4861                                         }
4862                                 }
4863                         }
4864
4865                         //now compare our list with the current selection, and add or remove as necessary
4866                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4867                         TrackViewList tracks_to_add;
4868                         TrackViewList tracks_to_remove;
4869                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4870                                 if ( !_editor->selection->tracks.contains ( *i ) )
4871                                         tracks_to_add.push_back ( *i );
4872                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4873                                 if ( !grouped_add.contains ( *i ) )
4874                                         tracks_to_remove.push_back ( *i );
4875                         _editor->selection->add(tracks_to_add);
4876                         _editor->selection->remove(tracks_to_remove);
4877
4878                 }
4879         }
4880         break;
4881
4882         case SelectionStartTrim:
4883
4884                 end = _editor->selection->time[_editor->clicked_selection].end;
4885
4886                 if (pending_position > end) {
4887                         start = end;
4888                 } else {
4889                         start = pending_position;
4890                 }
4891                 break;
4892
4893         case SelectionEndTrim:
4894
4895                 start = _editor->selection->time[_editor->clicked_selection].start;
4896
4897                 if (pending_position < start) {
4898                         end = start;
4899                 } else {
4900                         end = pending_position;
4901                 }
4902
4903                 break;
4904
4905         case SelectionMove:
4906
4907                 start = _editor->selection->time[_editor->clicked_selection].start;
4908                 end = _editor->selection->time[_editor->clicked_selection].end;
4909
4910                 length = end - start;
4911                 distance = pending_position - start;
4912                 start = pending_position;
4913                 _editor->snap_to (start);
4914
4915                 end = start + length;
4916
4917                 break;
4918
4919         case SelectionExtend:
4920                 break;
4921         }
4922
4923         if (start != end) {
4924                 switch (_operation) {
4925                 case SelectionMove:
4926                         if (_time_selection_at_start) {
4927                                 _editor->selection->move_time (distance);
4928                         }
4929                         break;
4930                 default:
4931                         _editor->selection->replace (_editor->clicked_selection, start, end);
4932                 }
4933         }
4934
4935         if (_operation == SelectionMove) {
4936                 show_verbose_cursor_time(start);
4937         } else {
4938                 show_verbose_cursor_time(pending_position);
4939         }
4940 }
4941
4942 void
4943 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4944 {
4945         Session* s = _editor->session();
4946
4947         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
4948         if (movement_occurred) {
4949                 motion (event, false);
4950                 /* XXX this is not object-oriented programming at all. ick */
4951                 if (_editor->selection->time.consolidate()) {
4952                         _editor->selection->TimeChanged ();
4953                 }
4954
4955                 /* XXX what if its a music time selection? */
4956                 if (s) {
4957                         if (s->get_play_range() && s->transport_rolling()) {
4958                                 s->request_play_range (&_editor->selection->time, true);
4959                         } else if (!s->config.get_external_sync()) {
4960                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
4961                                         if (_operation == SelectionEndTrim)
4962                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4963                                         else
4964                                                 s->request_locate (_editor->get_selection().time.start());
4965                                 }
4966                         }
4967
4968                         if (_editor->get_selection().time.length() != 0) {
4969                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
4970                         } else {
4971                                 s->clear_range_selection ();
4972                         }
4973                 }
4974
4975         } else {
4976                 /* just a click, no pointer movement.
4977                  */
4978
4979                 if (_operation == SelectionExtend) {
4980                         if (_time_selection_at_start) {
4981                                 framepos_t pos = adjusted_current_frame (event, false);
4982                                 framepos_t start = min (pos, start_at_start);
4983                                 framepos_t end = max (pos, end_at_start);
4984                                 _editor->selection->set (start, end);
4985                         }
4986                 } else {
4987                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4988                                 if (_editor->clicked_selection) {
4989                                         _editor->selection->remove (_editor->clicked_selection);
4990                                 }
4991                         } else {
4992                                 if (!_editor->clicked_selection) {
4993                                         _editor->selection->clear_time();
4994                                 }
4995                         }
4996                 }
4997
4998                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4999                         _editor->selection->set (_editor->clicked_axisview);
5000                 }
5001
5002                 if (s && s->get_play_range () && s->transport_rolling()) {
5003                         s->request_stop (false, false);
5004                 }
5005
5006         }
5007
5008         _editor->stop_canvas_autoscroll ();
5009         _editor->clicked_selection = 0;
5010         _editor->commit_reversible_selection_op ();
5011 }
5012
5013 void
5014 SelectionDrag::aborted (bool)
5015 {
5016         /* XXX: TODO */
5017 }
5018
5019 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5020         : Drag (e, i, false),
5021           _operation (o),
5022           _copy (false)
5023 {
5024         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5025
5026         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5027                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5028                                                                       physical_screen_height (_editor->get_window())));
5029         _drag_rect->hide ();
5030
5031         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5032         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5033 }
5034
5035 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5036 {
5037         /* normal canvas items will be cleaned up when their parent group is deleted. But
5038            this item is created as the child of a long-lived parent group, and so we
5039            need to explicitly delete it.
5040         */
5041         delete _drag_rect;
5042 }
5043
5044 void
5045 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5046 {
5047         if (_editor->session() == 0) {
5048                 return;
5049         }
5050
5051         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5052
5053         if (!_editor->temp_location) {
5054                 _editor->temp_location = new Location (*_editor->session());
5055         }
5056
5057         switch (_operation) {
5058         case CreateSkipMarker:
5059         case CreateRangeMarker:
5060         case CreateTransportMarker:
5061         case CreateCDMarker:
5062
5063                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5064                         _copy = true;
5065                 } else {
5066                         _copy = false;
5067                 }
5068                 cursor = _editor->cursors()->selector;
5069                 break;
5070         }
5071
5072         Drag::start_grab (event, cursor);
5073
5074         show_verbose_cursor_time (adjusted_current_frame (event));
5075 }
5076
5077 void
5078 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5079 {
5080         framepos_t start = 0;
5081         framepos_t end = 0;
5082         ArdourCanvas::Rectangle *crect;
5083
5084         switch (_operation) {
5085         case CreateSkipMarker:
5086                 crect = _editor->range_bar_drag_rect;
5087                 break;
5088         case CreateRangeMarker:
5089                 crect = _editor->range_bar_drag_rect;
5090                 break;
5091         case CreateTransportMarker:
5092                 crect = _editor->transport_bar_drag_rect;
5093                 break;
5094         case CreateCDMarker:
5095                 crect = _editor->cd_marker_bar_drag_rect;
5096                 break;
5097         default:
5098                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5099                 return;
5100                 break;
5101         }
5102
5103         framepos_t const pf = adjusted_current_frame (event);
5104
5105         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5106                 framepos_t grab = grab_frame ();
5107                 _editor->snap_to (grab);
5108
5109                 if (pf < grab_frame()) {
5110                         start = pf;
5111                         end = grab;
5112                 } else {
5113                         end = pf;
5114                         start = grab;
5115                 }
5116
5117                 /* first drag: Either add to the selection
5118                    or create a new selection.
5119                 */
5120
5121                 if (first_move) {
5122
5123                         _editor->temp_location->set (start, end);
5124
5125                         crect->show ();
5126
5127                         update_item (_editor->temp_location);
5128                         _drag_rect->show();
5129                         //_drag_rect->raise_to_top();
5130
5131                 }
5132         }
5133
5134         if (start != end) {
5135                 _editor->temp_location->set (start, end);
5136
5137                 double x1 = _editor->sample_to_pixel (start);
5138                 double x2 = _editor->sample_to_pixel (end);
5139                 crect->set_x0 (x1);
5140                 crect->set_x1 (x2);
5141
5142                 update_item (_editor->temp_location);
5143         }
5144
5145         show_verbose_cursor_time (pf);
5146
5147 }
5148
5149 void
5150 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5151 {
5152         Location * newloc = 0;
5153         string rangename;
5154         int flags;
5155
5156         if (movement_occurred) {
5157                 motion (event, false);
5158                 _drag_rect->hide();
5159
5160                 switch (_operation) {
5161                 case CreateSkipMarker:
5162                 case CreateRangeMarker:
5163                 case CreateCDMarker:
5164                     {
5165                         XMLNode &before = _editor->session()->locations()->get_state();
5166                         if (_operation == CreateSkipMarker) {
5167                                 _editor->begin_reversible_command (_("new skip marker"));
5168                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5169                                 flags = Location::IsRangeMarker | Location::IsSkip;
5170                                 _editor->range_bar_drag_rect->hide();
5171                         } else if (_operation == CreateCDMarker) {
5172                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5173                                 _editor->begin_reversible_command (_("new CD marker"));
5174                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5175                                 _editor->cd_marker_bar_drag_rect->hide();
5176                         } else {
5177                                 _editor->begin_reversible_command (_("new skip marker"));
5178                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5179                                 flags = Location::IsRangeMarker;
5180                                 _editor->range_bar_drag_rect->hide();
5181                         }
5182                         newloc = new Location (
5183                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5184                                 );
5185
5186                         _editor->session()->locations()->add (newloc, true);
5187                         XMLNode &after = _editor->session()->locations()->get_state();
5188                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5189                         _editor->commit_reversible_command ();
5190                         break;
5191                     }
5192
5193                 case CreateTransportMarker:
5194                         // popup menu to pick loop or punch
5195                         _editor->new_transport_marker_context_menu (&event->button, _item);
5196                         break;
5197                 }
5198
5199         } else {
5200
5201                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5202
5203                 if (_operation == CreateTransportMarker) {
5204
5205                         /* didn't drag, so just locate */
5206
5207                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5208
5209                 } else if (_operation == CreateCDMarker) {
5210
5211                         /* didn't drag, but mark is already created so do
5212                          * nothing */
5213
5214                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5215
5216                         framepos_t start;
5217                         framepos_t end;
5218
5219                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5220
5221                         if (end == max_framepos) {
5222                                 end = _editor->session()->current_end_frame ();
5223                         }
5224
5225                         if (start == max_framepos) {
5226                                 start = _editor->session()->current_start_frame ();
5227                         }
5228
5229                         switch (_editor->mouse_mode) {
5230                         case MouseObject:
5231                                 /* find the two markers on either side and then make the selection from it */
5232                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5233                                 break;
5234
5235                         case MouseRange:
5236                                 /* find the two markers on either side of the click and make the range out of it */
5237                                 _editor->selection->set (start, end);
5238                                 break;
5239
5240                         default:
5241                                 break;
5242                         }
5243                 }
5244         }
5245
5246         _editor->stop_canvas_autoscroll ();
5247 }
5248
5249 void
5250 RangeMarkerBarDrag::aborted (bool movement_occured)
5251 {
5252         if (movement_occured) {
5253                 _drag_rect->hide ();
5254         }
5255 }
5256
5257 void
5258 RangeMarkerBarDrag::update_item (Location* location)
5259 {
5260         double const x1 = _editor->sample_to_pixel (location->start());
5261         double const x2 = _editor->sample_to_pixel (location->end());
5262
5263         _drag_rect->set_x0 (x1);
5264         _drag_rect->set_x1 (x2);
5265 }
5266
5267 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5268         : Drag (e, i)
5269         , _cumulative_dx (0)
5270         , _cumulative_dy (0)
5271 {
5272         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5273
5274         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5275         assert (_primary);
5276         _region = &_primary->region_view ();
5277         _note_height = _region->midi_stream_view()->note_height ();
5278 }
5279
5280 void
5281 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5282 {
5283         Drag::start_grab (event);
5284         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5285
5286         if (!(_was_selected = _primary->selected())) {
5287
5288                 /* tertiary-click means extend selection - we'll do that on button release,
5289                    so don't add it here, because otherwise we make it hard to figure
5290                    out the "extend-to" range.
5291                 */
5292
5293                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5294
5295                 if (!extend) {
5296                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5297
5298                         if (add) {
5299                                 _region->note_selected (_primary, true);
5300                         } else {
5301                                 _region->unique_select (_primary);
5302                         }
5303
5304                         _editor->begin_reversible_selection_op(X_("Select Note Press"));
5305                         _editor->commit_reversible_selection_op();
5306                 }
5307         }
5308 }
5309
5310 /** @return Current total drag x change in frames */
5311 frameoffset_t
5312 NoteDrag::total_dx (const guint state) const
5313 {
5314         /* dx in frames */
5315         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5316
5317         /* primary note time */
5318         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5319
5320         /* new time of the primary note in session frames */
5321         frameoffset_t st = n + dx + snap_delta (state);
5322
5323         framepos_t const rp = _region->region()->position ();
5324
5325         /* prevent the note being dragged earlier than the region's position */
5326         st = max (st, rp);
5327
5328         /* possibly snap and return corresponding delta */
5329
5330         bool snap = true;
5331
5332         if (ArdourKeyboard::indicates_snap (state)) {
5333                 if (_editor->snap_mode () != SnapOff) {
5334                         snap = false;
5335                 }
5336         } else {
5337                 if (_editor->snap_mode () == SnapOff) {
5338                         snap = false;
5339                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5340                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5341                                 snap = true;
5342                         }
5343                 }
5344         }
5345
5346         frameoffset_t ret;
5347         if (snap) {
5348                 ret =  _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5349         } else {
5350                 ret = st - n - snap_delta (state);
5351         }
5352         return ret;
5353 }
5354
5355 /** @return Current total drag y change in note number */
5356 int8_t
5357 NoteDrag::total_dy () const
5358 {
5359         MidiStreamView* msv = _region->midi_stream_view ();
5360         double const y = _region->midi_view()->y_position ();
5361         /* new current note */
5362         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5363         /* clamp */
5364         n = max (msv->lowest_note(), n);
5365         n = min (msv->highest_note(), n);
5366         /* and work out delta */
5367         return n - msv->y_to_note (grab_y() - y);
5368 }
5369
5370 void
5371 NoteDrag::motion (GdkEvent * event, bool)
5372 {
5373         /* Total change in x and y since the start of the drag */
5374         frameoffset_t const dx = total_dx (event->button.state);
5375         int8_t const dy = total_dy ();
5376
5377         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5378         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5379         double const tdy = -dy * _note_height - _cumulative_dy;
5380
5381         if (tdx || tdy) {
5382                 _cumulative_dx += tdx;
5383                 _cumulative_dy += tdy;
5384
5385                 int8_t note_delta = total_dy();
5386
5387                 _region->move_selection (tdx, tdy, note_delta);
5388
5389                 /* the new note value may be the same as the old one, but we
5390                  * don't know what that means because the selection may have
5391                  * involved more than one note and we might be doing something
5392                  * odd with them. so show the note value anyway, always.
5393                  */
5394
5395                 char buf[12];
5396                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5397
5398                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5399                           (int) floor ((double)new_note));
5400
5401                 show_verbose_cursor_text (buf);
5402         }
5403 }
5404
5405 void
5406 NoteDrag::finished (GdkEvent* ev, bool moved)
5407 {
5408         if (!moved) {
5409                 /* no motion - select note */
5410
5411                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5412                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5413
5414                         bool changed = false;
5415
5416                         if (_was_selected) {
5417                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5418                                 if (add) {
5419                                         _region->note_deselected (_primary);
5420                                         changed = true;
5421                                 } else {
5422                                         _region->unique_select (_primary);
5423                                 }
5424                         } else {
5425                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5426                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5427
5428                                 if (!extend && !add && _region->selection_size() > 1) {
5429                                         _region->unique_select (_primary);
5430                                         changed = true;
5431                                 } else if (extend) {
5432                                         _region->note_selected (_primary, true, true);
5433                                         changed = true;
5434                                 } else {
5435                                         /* it was added during button press */
5436                                 }
5437                         }
5438
5439                         if (changed) {
5440                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5441                                 _editor->commit_reversible_selection_op();
5442                         }
5443                 }
5444         } else {
5445                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5446         }
5447 }
5448
5449 void
5450 NoteDrag::aborted (bool)
5451 {
5452         /* XXX: TODO */
5453 }
5454
5455 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5456 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5457         : Drag (editor, atv->base_item ())
5458         , _ranges (r)
5459         , _y_origin (atv->y_position())
5460         , _nothing_to_drag (false)
5461 {
5462         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5463         setup (atv->lines ());
5464 }
5465
5466 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5467 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5468         : Drag (editor, rv->get_canvas_group ())
5469         , _ranges (r)
5470         , _y_origin (rv->get_time_axis_view().y_position())
5471         , _nothing_to_drag (false)
5472         , _integral (false)
5473 {
5474         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5475
5476         list<boost::shared_ptr<AutomationLine> > lines;
5477
5478         AudioRegionView*      audio_view;
5479         AutomationRegionView* automation_view;
5480         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5481                 lines.push_back (audio_view->get_gain_line ());
5482         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5483                 lines.push_back (automation_view->line ());
5484                 _integral = true;
5485         } else {
5486                 error << _("Automation range drag created for invalid region type") << endmsg;
5487         }
5488
5489         setup (lines);
5490 }
5491
5492 /** @param lines AutomationLines to drag.
5493  *  @param offset Offset from the session start to the points in the AutomationLines.
5494  */
5495 void
5496 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5497 {
5498         /* find the lines that overlap the ranges being dragged */
5499         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5500         while (i != lines.end ()) {
5501                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5502                 ++j;
5503
5504                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5505
5506                 /* check this range against all the AudioRanges that we are using */
5507                 list<AudioRange>::const_iterator k = _ranges.begin ();
5508                 while (k != _ranges.end()) {
5509                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5510                                 break;
5511                         }
5512                         ++k;
5513                 }
5514
5515                 /* add it to our list if it overlaps at all */
5516                 if (k != _ranges.end()) {
5517                         Line n;
5518                         n.line = *i;
5519                         n.state = 0;
5520                         n.range = r;
5521                         _lines.push_back (n);
5522                 }
5523
5524                 i = j;
5525         }
5526
5527         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5528 }
5529
5530 double
5531 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5532 {
5533         return 1.0 - ((global_y - _y_origin) / line->height());
5534 }
5535
5536 double
5537 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5538 {
5539         const double v = list->eval(x);
5540         return _integral ? rint(v) : v;
5541 }
5542
5543 void
5544 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5545 {
5546         Drag::start_grab (event, cursor);
5547
5548         /* Get line states before we start changing things */
5549         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5550                 i->state = &i->line->get_state ();
5551                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5552         }
5553
5554         if (_ranges.empty()) {
5555
5556                 /* No selected time ranges: drag all points */
5557                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5558                         uint32_t const N = i->line->npoints ();
5559                         for (uint32_t j = 0; j < N; ++j) {
5560                                 i->points.push_back (i->line->nth (j));
5561                         }
5562                 }
5563
5564         }
5565
5566         if (_nothing_to_drag) {
5567                 return;
5568         }
5569 }
5570
5571 void
5572 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5573 {
5574         if (_nothing_to_drag && !first_move) {
5575                 return;
5576         }
5577
5578         if (first_move) {
5579                 _editor->begin_reversible_command (_("automation range move"));
5580
5581                 if (!_ranges.empty()) {
5582
5583                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5584
5585                                 framecnt_t const half = (i->start + i->end) / 2;
5586
5587                                 /* find the line that this audio range starts in */
5588                                 list<Line>::iterator j = _lines.begin();
5589                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5590                                         ++j;
5591                                 }
5592
5593                                 if (j != _lines.end()) {
5594                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5595
5596                                 /* j is the line that this audio range starts in; fade into it;
5597                                    64 samples length plucked out of thin air.
5598                                 */
5599
5600                                         framepos_t a = i->start + 64;
5601                                         if (a > half) {
5602                                                 a = half;
5603                                         }
5604
5605                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5606                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5607
5608                                         XMLNode &before = the_list->get_state();
5609                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5610                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5611
5612                                         if (add_p || add_q) {
5613                                                 _editor->session()->add_command (
5614                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5615                                         }
5616                                 }
5617
5618                                 /* same thing for the end */
5619
5620                                 j = _lines.begin();
5621                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5622                                         ++j;
5623                                 }
5624
5625                                 if (j != _lines.end()) {
5626                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5627
5628                                         /* j is the line that this audio range starts in; fade out of it;
5629                                            64 samples length plucked out of thin air.
5630                                         */
5631
5632                                         framepos_t b = i->end - 64;
5633                                         if (b < half) {
5634                                                 b = half;
5635                                         }
5636
5637                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5638                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5639
5640                                         XMLNode &before = the_list->get_state();
5641                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5642                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5643
5644                                         if (add_p || add_q) {
5645                                                 _editor->session()->add_command (
5646                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5647                                         }
5648                                 }
5649                         }
5650
5651                         _nothing_to_drag = true;
5652
5653                         /* Find all the points that should be dragged and put them in the relevant
5654                            points lists in the Line structs.
5655                         */
5656
5657                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5658
5659                                 uint32_t const N = i->line->npoints ();
5660                                 for (uint32_t j = 0; j < N; ++j) {
5661
5662                                         /* here's a control point on this line */
5663                                         ControlPoint* p = i->line->nth (j);
5664                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5665
5666                                         /* see if it's inside a range */
5667                                         list<AudioRange>::const_iterator k = _ranges.begin ();
5668                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5669                                                 ++k;
5670                                         }
5671
5672                                         if (k != _ranges.end()) {
5673                                                 /* dragging this point */
5674                                                 _nothing_to_drag = false;
5675                                                 i->points.push_back (p);
5676                                         }
5677                                 }
5678                         }
5679                 }
5680
5681                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5682                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5683                 }
5684         }
5685
5686         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5687                 float const f = y_fraction (l->line, current_pointer_y());
5688                 /* we are ignoring x position for this drag, so we can just pass in anything */
5689                 uint32_t ignored;
5690                 l->line->drag_motion (0, f, true, false, ignored);
5691                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5692         }
5693 }
5694
5695 void
5696 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5697 {
5698         if (_nothing_to_drag || !motion_occurred) {
5699                 return;
5700         }
5701
5702         motion (event, false);
5703         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5704                 i->line->end_drag (false, 0);
5705         }
5706
5707         _editor->commit_reversible_command ();
5708 }
5709
5710 void
5711 AutomationRangeDrag::aborted (bool)
5712 {
5713         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5714                 i->line->reset ();
5715         }
5716 }
5717
5718 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5719         : view (v)
5720         , initial_time_axis_view (itav)
5721 {
5722         /* note that time_axis_view may be null if the regionview was created
5723          * as part of a copy operation.
5724          */
5725         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5726         layer = v->region()->layer ();
5727         initial_y = v->get_canvas_group()->position().y;
5728         initial_playlist = v->region()->playlist ();
5729         initial_position = v->region()->position ();
5730         initial_end = v->region()->position () + v->region()->length ();
5731 }
5732
5733 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5734         : Drag (e, i->canvas_item ())
5735         , _region_view (r)
5736         , _patch_change (i)
5737         , _cumulative_dx (0)
5738 {
5739         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5740                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5741                                                    grab_frame()));
5742 }
5743
5744 void
5745 PatchChangeDrag::motion (GdkEvent* ev, bool)
5746 {
5747         framepos_t f = adjusted_current_frame (ev);
5748         boost::shared_ptr<Region> r = _region_view->region ();
5749         f = max (f, r->position ());
5750         f = min (f, r->last_frame ());
5751
5752         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5753         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5754         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5755         _cumulative_dx = dxu;
5756 }
5757
5758 void
5759 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5760 {
5761         if (!movement_occurred) {
5762                 return;
5763         }
5764
5765         boost::shared_ptr<Region> r (_region_view->region ());
5766         framepos_t f = adjusted_current_frame (ev);
5767         f = max (f, r->position ());
5768         f = min (f, r->last_frame ());
5769
5770         _region_view->move_patch_change (
5771                 *_patch_change,
5772                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5773                 );
5774 }
5775
5776 void
5777 PatchChangeDrag::aborted (bool)
5778 {
5779         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5780 }
5781
5782 void
5783 PatchChangeDrag::setup_pointer_frame_offset ()
5784 {
5785         boost::shared_ptr<Region> region = _region_view->region ();
5786         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5787 }
5788
5789 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5790         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5791         , _region_view (rv)
5792 {
5793
5794 }
5795
5796 void
5797 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5798 {
5799         _region_view->update_drag_selection (
5800                 x1, x2, y1, y2,
5801                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5802 }
5803
5804 void
5805 MidiRubberbandSelectDrag::deselect_things ()
5806 {
5807         /* XXX */
5808 }
5809
5810 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5811         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5812         , _region_view (rv)
5813 {
5814         _vertical_only = true;
5815 }
5816
5817 void
5818 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5819 {
5820         double const y = _region_view->midi_view()->y_position ();
5821
5822         y1 = max (0.0, y1 - y);
5823         y2 = max (0.0, y2 - y);
5824
5825         _region_view->update_vertical_drag_selection (
5826                 y1,
5827                 y2,
5828                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5829                 );
5830 }
5831
5832 void
5833 MidiVerticalSelectDrag::deselect_things ()
5834 {
5835         /* XXX */
5836 }
5837
5838 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5839         : RubberbandSelectDrag (e, i)
5840 {
5841
5842 }
5843
5844 void
5845 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5846 {
5847         if (drag_in_progress) {
5848                 /* We just want to select things at the end of the drag, not during it */
5849                 return;
5850         }
5851
5852         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5853
5854         _editor->begin_reversible_selection_op (X_("rubberband selection"));
5855
5856         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5857
5858         _editor->commit_reversible_selection_op ();
5859 }
5860
5861 void
5862 EditorRubberbandSelectDrag::deselect_things ()
5863 {
5864         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5865
5866         _editor->selection->clear_tracks();
5867         _editor->selection->clear_regions();
5868         _editor->selection->clear_points ();
5869         _editor->selection->clear_lines ();
5870         _editor->selection->clear_midi_notes ();
5871
5872         _editor->commit_reversible_selection_op();
5873 }
5874
5875 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5876         : Drag (e, i)
5877         , _region_view (rv)
5878         , _drag_rect (0)
5879 {
5880         _note[0] = _note[1] = 0;
5881 }
5882
5883 NoteCreateDrag::~NoteCreateDrag ()
5884 {
5885         delete _drag_rect;
5886 }
5887
5888 framecnt_t
5889 NoteCreateDrag::grid_frames (framepos_t t) const
5890 {
5891         bool success;
5892         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5893         if (!success) {
5894                 grid_beats = Evoral::Beats(1);
5895         }
5896
5897         return _region_view->region_beats_to_region_frames (grid_beats);
5898 }
5899
5900 void
5901 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5902 {
5903         Drag::start_grab (event, cursor);
5904
5905         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5906
5907         framepos_t pf = _drags->current_pointer_frame ();
5908         framecnt_t const g = grid_frames (pf);
5909
5910         /* Hack so that we always snap to the note that we are over, instead of snapping
5911            to the next one if we're more than halfway through the one we're over.
5912         */
5913         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5914                 pf -= g / 2;
5915         }
5916
5917         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5918         _note[1] = _note[0];
5919
5920         MidiStreamView* sv = _region_view->midi_stream_view ();
5921         double const x = _editor->sample_to_pixel (_note[0]);
5922         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5923
5924         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5925         _drag_rect->set_outline_all ();
5926         _drag_rect->set_outline_color (0xffffff99);
5927         _drag_rect->set_fill_color (0xffffff66);
5928 }
5929
5930 void
5931 NoteCreateDrag::motion (GdkEvent* event, bool)
5932 {
5933         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5934         double const x0 = _editor->sample_to_pixel (_note[0]);
5935         double const x1 = _editor->sample_to_pixel (_note[1]);
5936         _drag_rect->set_x0 (std::min(x0, x1));
5937         _drag_rect->set_x1 (std::max(x0, x1));
5938 }
5939
5940 void
5941 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5942 {
5943         if (!had_movement) {
5944                 return;
5945         }
5946
5947         framepos_t const start = min (_note[0], _note[1]);
5948         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5949
5950         framecnt_t const g = grid_frames (start);
5951         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5952
5953         if (_editor->snap_mode() == SnapNormal && length < g) {
5954                 length = g;
5955         }
5956
5957         Evoral::Beats length_beats = max (
5958                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5959
5960         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5961 }
5962
5963 double
5964 NoteCreateDrag::y_to_region (double y) const
5965 {
5966         double x = 0;
5967         _region_view->get_canvas_group()->canvas_to_item (x, y);
5968         return y;
5969 }
5970
5971 void
5972 NoteCreateDrag::aborted (bool)
5973 {
5974
5975 }
5976
5977 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5978         : Drag (e, i)
5979         , arv (rv)
5980         , start (start_yn)
5981 {
5982         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5983 }
5984
5985 void
5986 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5987 {
5988         Drag::start_grab (event, cursor);
5989 }
5990
5991 void
5992 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5993 {
5994         double distance;
5995         double new_length;
5996         framecnt_t len;
5997
5998         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5999
6000         if (start) {
6001                 distance = _drags->current_pointer_x() - grab_x();
6002                 len = ar->fade_in()->back()->when;
6003         } else {
6004                 distance = grab_x() - _drags->current_pointer_x();
6005                 len = ar->fade_out()->back()->when;
6006         }
6007
6008         /* how long should it be ? */
6009
6010         new_length = len + _editor->pixel_to_sample (distance);
6011
6012         /* now check with the region that this is legal */
6013
6014         new_length = ar->verify_xfade_bounds (new_length, start);
6015
6016         if (start) {
6017                 arv->reset_fade_in_shape_width (ar, new_length);
6018         } else {
6019                 arv->reset_fade_out_shape_width (ar, new_length);
6020         }
6021 }
6022
6023 void
6024 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6025 {
6026         double distance;
6027         double new_length;
6028         framecnt_t len;
6029
6030         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6031
6032         if (start) {
6033                 distance = _drags->current_pointer_x() - grab_x();
6034                 len = ar->fade_in()->back()->when;
6035         } else {
6036                 distance = grab_x() - _drags->current_pointer_x();
6037                 len = ar->fade_out()->back()->when;
6038         }
6039
6040         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6041
6042         _editor->begin_reversible_command ("xfade trim");
6043         ar->playlist()->clear_owned_changes ();
6044
6045         if (start) {
6046                 ar->set_fade_in_length (new_length);
6047         } else {
6048                 ar->set_fade_out_length (new_length);
6049         }
6050
6051         /* Adjusting the xfade may affect other regions in the playlist, so we need
6052            to get undo Commands from the whole playlist rather than just the
6053            region.
6054         */
6055
6056         vector<Command*> cmds;
6057         ar->playlist()->rdiff (cmds);
6058         _editor->session()->add_commands (cmds);
6059         _editor->commit_reversible_command ();
6060
6061 }
6062
6063 void
6064 CrossfadeEdgeDrag::aborted (bool)
6065 {
6066         if (start) {
6067                 // arv->redraw_start_xfade ();
6068         } else {
6069                 // arv->redraw_end_xfade ();
6070         }
6071 }
6072
6073 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6074         : Drag (e, item, true)
6075         , line (new EditorCursor (*e))
6076 {
6077         line->set_position (pos);
6078         line->show ();
6079 }
6080
6081 RegionCutDrag::~RegionCutDrag ()
6082 {
6083         delete line;
6084 }
6085
6086 void
6087 RegionCutDrag::motion (GdkEvent*, bool)
6088 {
6089         framepos_t where = _drags->current_pointer_frame();
6090         _editor->snap_to (where);
6091
6092         line->set_position (where);
6093 }
6094
6095 void
6096 RegionCutDrag::finished (GdkEvent*, bool)
6097 {
6098         _editor->get_track_canvas()->canvas()->re_enter();
6099
6100         framepos_t pos = _drags->current_pointer_frame();
6101
6102         line->hide ();
6103
6104         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6105
6106         if (rs.empty()) {
6107                 return;
6108         }
6109
6110         _editor->split_regions_at (pos, rs);
6111 }
6112
6113 void
6114 RegionCutDrag::aborted (bool)
6115 {
6116 }