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