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