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