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