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