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