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