TempoMap - rename some methods.
[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                         const framepos_t frame = map.frame_at_beat (beat);
3291                         _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3292                                                        , beat, bbt, frame, _real_section->position_lock_style());
3293                         if (!_real_section) {
3294                                 aborted (true);
3295                                 return;
3296                         }
3297
3298                 }
3299                 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3300                 if (_real_section->position_lock_style() != AudioTime) {
3301                         _editor->set_snap_to (SnapToBar);
3302                         _editor->set_snap_mode (SnapNormal);
3303                 }
3304         }
3305
3306         framepos_t pf = adjusted_current_frame (event);
3307
3308         if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3309                 /* never snap to music for audio locked */
3310                 pf = adjusted_current_frame (event, false);
3311         }
3312
3313         _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3314
3315         /* fake marker meeds to stay under the mouse, unlike the real one. */
3316         _marker->set_position (adjusted_current_frame (event, false));
3317
3318         show_verbose_cursor_time (_real_section->frame());
3319 }
3320
3321 void
3322 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3323 {
3324         if (!movement_occurred) {
3325                 if (was_double_click()) {
3326                         _editor->edit_meter_marker (*_marker);
3327                 }
3328                 return;
3329         }
3330
3331         /* reinstate old snap setting */
3332         _editor->set_snap_to (_old_snap_type);
3333         _editor->set_snap_mode (_old_snap_mode);
3334
3335         TempoMap& map (_editor->session()->tempo_map());
3336
3337         XMLNode &after = map.get_state();
3338         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3339         _editor->commit_reversible_command ();
3340
3341         // delete the dummy marker we used for visual representation while moving.
3342         // a new visual marker will show up automatically.
3343         delete _marker;
3344 }
3345
3346 void
3347 MeterMarkerDrag::aborted (bool moved)
3348 {
3349         _marker->set_position (_marker->meter().frame ());
3350         if (moved) {
3351                 /* reinstate old snap setting */
3352                 _editor->set_snap_to (_old_snap_type);
3353                 _editor->set_snap_mode (_old_snap_mode);
3354
3355                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3356                 // delete the dummy marker we used for visual representation while moving.
3357                 // a new visual marker will show up automatically.
3358                 delete _marker;
3359         }
3360 }
3361
3362 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3363         : Drag (e, i)
3364         , _copy (c)
3365         , _grab_bpm (0.0)
3366         , before_state (0)
3367 {
3368         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3369
3370         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3371         _real_section = &_marker->tempo();
3372         _movable = !_real_section->initial();
3373         _grab_bpm = _real_section->note_types_per_minute();
3374         assert (_marker);
3375 }
3376
3377 void
3378 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3379 {
3380         Drag::start_grab (event, cursor);
3381         if (!_real_section->active()) {
3382                 show_verbose_cursor_text (_("inactive"));
3383         } else {
3384                 show_verbose_cursor_time (adjusted_current_frame (event));
3385         }
3386 }
3387
3388 void
3389 TempoMarkerDrag::setup_pointer_frame_offset ()
3390 {
3391         _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3392 }
3393
3394 void
3395 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3396 {
3397         if (!_real_section->active()) {
3398                 return;
3399         }
3400
3401         if (first_move) {
3402
3403                 // mvc drag - create a dummy marker to catch events, hide it.
3404
3405                 char name[64];
3406                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3407
3408                 TempoSection section (_marker->tempo());
3409
3410                 _marker = new TempoMarker (
3411                         *_editor,
3412                         *_editor->tempo_group,
3413                         UIConfiguration::instance().color ("tempo marker"),
3414                         name,
3415                         *new TempoSection (_marker->tempo())
3416                         );
3417
3418                 /* use the new marker for the grab */
3419                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3420                 _marker->hide();
3421
3422                 TempoMap& map (_editor->session()->tempo_map());
3423                 /* get current state */
3424                 before_state = &map.get_state();
3425
3426                 if (!_copy) {
3427                         _editor->begin_reversible_command (_("move tempo mark"));
3428
3429                 } else {
3430                         const Tempo tempo (_marker->tempo());
3431                         const framepos_t frame = adjusted_current_frame (event) + 1;
3432                         const TempoSection::Type type = _real_section->type();
3433
3434                         _editor->begin_reversible_command (_("copy tempo mark"));
3435
3436                         if (_real_section->position_lock_style() == MusicTime) {
3437                                 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3438                                 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, type, MusicTime);
3439                         } else {
3440                                 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3441                         }
3442
3443                         if (!_real_section) {
3444                                 aborted (true);
3445                                 return;
3446                         }
3447                 }
3448
3449         }
3450
3451         if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3452                 /* use vertical movement to alter tempo .. should be log */
3453                 double new_bpm = max (1.5, _grab_bpm + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3454                 stringstream strs;
3455                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3456                 strs << new_bpm;
3457                 show_verbose_cursor_text (strs.str());
3458
3459         } else if (_movable && !_real_section->locked_to_meter()) {
3460                 framepos_t pf;
3461
3462                 if (_editor->snap_musical()) {
3463                         /* we can't snap to a grid that we are about to move.
3464                          * gui_move_tempo() will sort out snap using the supplied beat divisions.
3465                         */
3466                         pf = adjusted_current_frame (event, false);
3467                 } else {
3468                         pf = adjusted_current_frame (event);
3469                 }
3470
3471                 TempoMap& map (_editor->session()->tempo_map());
3472
3473                 /* snap to beat is 1, snap to bar is -1 (sorry) */
3474                 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3475
3476                 map.gui_set_tempo_position (_real_section, pf, sub_num);
3477
3478                 show_verbose_cursor_time (_real_section->frame());
3479         }
3480         _marker->set_position (adjusted_current_frame (event, false));
3481 }
3482
3483 void
3484 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3485 {
3486         if (!_real_section->active()) {
3487                 return;
3488         }
3489         if (!movement_occurred) {
3490                 if (was_double_click()) {
3491                         _editor->edit_tempo_marker (*_marker);
3492                 }
3493                 return;
3494         }
3495
3496         TempoMap& map (_editor->session()->tempo_map());
3497
3498         XMLNode &after = map.get_state();
3499         _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3500         _editor->commit_reversible_command ();
3501
3502         // delete the dummy marker we used for visual representation while moving.
3503         // a new visual marker will show up automatically.
3504         delete _marker;
3505 }
3506
3507 void
3508 TempoMarkerDrag::aborted (bool moved)
3509 {
3510         _marker->set_position (_marker->tempo().frame());
3511         if (moved) {
3512                 TempoMap& map (_editor->session()->tempo_map());
3513                 map.set_state (*before_state, Stateful::current_state_version);
3514                 // delete the dummy (hidden) marker we used for events while moving.
3515                 delete _marker;
3516         }
3517 }
3518
3519 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3520         : Drag (e, i)
3521         , _grab_qn (0.0)
3522         , _tempo (0)
3523         , before_state (0)
3524 {
3525         DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3526
3527 }
3528
3529 void
3530 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3531 {
3532         Drag::start_grab (event, cursor);
3533         TempoMap& map (_editor->session()->tempo_map());
3534         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3535         ostringstream sstr;
3536
3537         sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).note_types_per_minute() << "\n";
3538         sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3539         show_verbose_cursor_text (sstr.str());
3540         finished (event, false);
3541 }
3542
3543 void
3544 BBTRulerDrag::setup_pointer_frame_offset ()
3545 {
3546         TempoMap& map (_editor->session()->tempo_map());
3547         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3548         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3549         double beat = 0.0;
3550
3551         if (divisions > 0) {
3552                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3553         } else {
3554                 /* while it makes some sense for the user to determine the division to 'grab',
3555                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3556                    and the result over steep tempo curves. Use sixteenths.
3557                 */
3558                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3559         }
3560
3561         _grab_qn = map.quarter_note_at_beat (beat);
3562
3563         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3564
3565 }
3566
3567 void
3568 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3569 {
3570         TempoMap& map (_editor->session()->tempo_map());
3571
3572         if (first_move) {
3573                 /* get current state */
3574                 before_state = &map.get_state();
3575                 _editor->begin_reversible_command (_("stretch tempo"));
3576         }
3577
3578         framepos_t pf;
3579
3580         if (_editor->snap_musical()) {
3581                 pf = adjusted_current_frame (event, false);
3582         } else {
3583                 pf = adjusted_current_frame (event);
3584         }
3585
3586         if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3587                 /* adjust previous tempo to match pointer frame */
3588                 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3589         }
3590         ostringstream sstr;
3591         sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).note_types_per_minute() << "\n";
3592         sstr << "<" << fixed << setprecision(3) << _tempo->note_types_per_minute();
3593         show_verbose_cursor_text (sstr.str());
3594 }
3595
3596 void
3597 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3598 {
3599         if (!movement_occurred) {
3600                 return;
3601         }
3602
3603         TempoMap& map (_editor->session()->tempo_map());
3604
3605         XMLNode &after = map.get_state();
3606         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3607         _editor->commit_reversible_command ();
3608 }
3609
3610 void
3611 BBTRulerDrag::aborted (bool moved)
3612 {
3613         if (moved) {
3614                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3615         }
3616 }
3617
3618
3619 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3620         : Drag (e, &c.track_canvas_item(), false)
3621         , _cursor (c)
3622         , _stop (s)
3623         , _grab_zoom (0.0)
3624 {
3625         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3626 }
3627
3628 /** Do all the things we do when dragging the playhead to make it look as though
3629  *  we have located, without actually doing the locate (because that would cause
3630  *  the diskstream buffers to be refilled, which is too slow).
3631  */
3632 void
3633 CursorDrag::fake_locate (framepos_t t)
3634 {
3635         if (_editor->session () == 0) {
3636                 return;
3637         }
3638
3639         _editor->playhead_cursor->set_position (t);
3640
3641         Session* s = _editor->session ();
3642         if (s->timecode_transmission_suspended ()) {
3643                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3644                 /* This is asynchronous so it will be sent "now"
3645                  */
3646                 s->send_mmc_locate (f);
3647                 /* These are synchronous and will be sent during the next
3648                    process cycle
3649                 */
3650                 s->queue_full_time_code ();
3651                 s->queue_song_position_pointer ();
3652         }
3653
3654         show_verbose_cursor_time (t);
3655         _editor->UpdateAllTransportClocks (t);
3656 }
3657
3658 void
3659 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3660 {
3661         Drag::start_grab (event, c);
3662         setup_snap_delta (_editor->playhead_cursor->current_frame ());
3663
3664         _grab_zoom = _editor->samples_per_pixel;
3665
3666         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3667
3668         _editor->snap_to_with_modifier (where, event);
3669
3670         _editor->_dragging_playhead = true;
3671
3672         Session* s = _editor->session ();
3673
3674         /* grab the track canvas item as well */
3675
3676         _cursor.track_canvas_item().grab();
3677
3678         if (s) {
3679                 if (_was_rolling && _stop) {
3680                         s->request_stop ();
3681                 }
3682
3683                 if (s->is_auditioning()) {
3684                         s->cancel_audition ();
3685                 }
3686
3687
3688                 if (AudioEngine::instance()->connected()) {
3689
3690                         /* do this only if we're the engine is connected
3691                          * because otherwise this request will never be
3692                          * serviced and we'll busy wait forever. likewise,
3693                          * notice if we are disconnected while waiting for the
3694                          * request to be serviced.
3695                          */
3696
3697                         s->request_suspend_timecode_transmission ();
3698                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3699                                 /* twiddle our thumbs */
3700                         }
3701                 }
3702         }
3703
3704         fake_locate (where - snap_delta (event->button.state));
3705 }
3706
3707 void
3708 CursorDrag::motion (GdkEvent* event, bool)
3709 {
3710         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3711         _editor->snap_to_with_modifier (where, event);
3712         if (where != last_pointer_frame()) {
3713                 fake_locate (where - snap_delta (event->button.state));
3714         }
3715 }
3716
3717 void
3718 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3719 {
3720         _editor->_dragging_playhead = false;
3721
3722         _cursor.track_canvas_item().ungrab();
3723
3724         if (!movement_occurred && _stop) {
3725                 return;
3726         }
3727
3728         motion (event, false);
3729
3730         Session* s = _editor->session ();
3731         if (s) {
3732                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3733                 _editor->_pending_locate_request = true;
3734                 s->request_resume_timecode_transmission ();
3735         }
3736 }
3737
3738 void
3739 CursorDrag::aborted (bool)
3740 {
3741         _cursor.track_canvas_item().ungrab();
3742
3743         if (_editor->_dragging_playhead) {
3744                 _editor->session()->request_resume_timecode_transmission ();
3745                 _editor->_dragging_playhead = false;
3746         }
3747
3748         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3749 }
3750
3751 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3752         : RegionDrag (e, i, p, v)
3753 {
3754         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3755 }
3756
3757 void
3758 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3759 {
3760         Drag::start_grab (event, cursor);
3761
3762         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3763         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3764         setup_snap_delta (r->position ());
3765
3766         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3767 }
3768
3769 void
3770 FadeInDrag::setup_pointer_frame_offset ()
3771 {
3772         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3773         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3774         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3775 }
3776
3777 void
3778 FadeInDrag::motion (GdkEvent* event, bool)
3779 {
3780         framecnt_t fade_length;
3781
3782         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3783         _editor->snap_to_with_modifier (pos, event);
3784         pos -= snap_delta (event->button.state);
3785
3786         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3787
3788         if (pos < (region->position() + 64)) {
3789                 fade_length = 64; // this should be a minimum defined somewhere
3790         } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3791                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3792         } else {
3793                 fade_length = pos - region->position();
3794         }
3795
3796         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3797
3798                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3799
3800                 if (!tmp) {
3801                         continue;
3802                 }
3803
3804                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3805         }
3806
3807         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3808 }
3809
3810 void
3811 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3812 {
3813         if (!movement_occurred) {
3814                 return;
3815         }
3816
3817         framecnt_t fade_length;
3818         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3819         _editor->snap_to_with_modifier (pos, event);
3820         pos -= snap_delta (event->button.state);
3821
3822         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3823
3824         if (pos < (region->position() + 64)) {
3825                 fade_length = 64; // this should be a minimum defined somewhere
3826         } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3827                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3828         } else {
3829                 fade_length = pos - region->position();
3830         }
3831
3832         bool in_command = false;
3833
3834         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3835
3836                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3837
3838                 if (!tmp) {
3839                         continue;
3840                 }
3841
3842                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3843                 XMLNode &before = alist->get_state();
3844
3845                 tmp->audio_region()->set_fade_in_length (fade_length);
3846                 tmp->audio_region()->set_fade_in_active (true);
3847
3848                 if (!in_command) {
3849                         _editor->begin_reversible_command (_("change fade in length"));
3850                         in_command = true;
3851                 }
3852                 XMLNode &after = alist->get_state();
3853                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3854         }
3855
3856         if (in_command) {
3857                 _editor->commit_reversible_command ();
3858         }
3859 }
3860
3861 void
3862 FadeInDrag::aborted (bool)
3863 {
3864         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3865                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3866
3867                 if (!tmp) {
3868                         continue;
3869                 }
3870
3871                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3872         }
3873 }
3874
3875 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3876         : RegionDrag (e, i, p, v)
3877 {
3878         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3879 }
3880
3881 void
3882 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3883 {
3884         Drag::start_grab (event, cursor);
3885
3886         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3887         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3888         setup_snap_delta (r->last_frame ());
3889
3890         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3891 }
3892
3893 void
3894 FadeOutDrag::setup_pointer_frame_offset ()
3895 {
3896         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3897         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3898         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3899 }
3900
3901 void
3902 FadeOutDrag::motion (GdkEvent* event, bool)
3903 {
3904         framecnt_t fade_length;
3905
3906         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3907         _editor->snap_to_with_modifier (pos, event);
3908         pos -= snap_delta (event->button.state);
3909
3910         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3911
3912         if (pos > (region->last_frame() - 64)) {
3913                 fade_length = 64; // this should really be a minimum fade defined somewhere
3914         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3915                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3916         } else {
3917                 fade_length = region->last_frame() - pos;
3918         }
3919
3920         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3921
3922                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3923
3924                 if (!tmp) {
3925                         continue;
3926                 }
3927
3928                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3929         }
3930
3931         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3932 }
3933
3934 void
3935 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3936 {
3937         if (!movement_occurred) {
3938                 return;
3939         }
3940
3941         framecnt_t fade_length;
3942
3943         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3944         _editor->snap_to_with_modifier (pos, event);
3945         pos -= snap_delta (event->button.state);
3946
3947         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3948
3949         if (pos > (region->last_frame() - 64)) {
3950                 fade_length = 64; // this should really be a minimum fade defined somewhere
3951         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3952                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3953         } else {
3954                 fade_length = region->last_frame() - pos;
3955         }
3956
3957         bool in_command = false;
3958
3959         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3960
3961                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3962
3963                 if (!tmp) {
3964                         continue;
3965                 }
3966
3967                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3968                 XMLNode &before = alist->get_state();
3969
3970                 tmp->audio_region()->set_fade_out_length (fade_length);
3971                 tmp->audio_region()->set_fade_out_active (true);
3972
3973                 if (!in_command) {
3974                         _editor->begin_reversible_command (_("change fade out length"));
3975                         in_command = true;
3976                 }
3977                 XMLNode &after = alist->get_state();
3978                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3979         }
3980
3981         if (in_command) {
3982                 _editor->commit_reversible_command ();
3983         }
3984 }
3985
3986 void
3987 FadeOutDrag::aborted (bool)
3988 {
3989         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3990                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3991
3992                 if (!tmp) {
3993                         continue;
3994                 }
3995
3996                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3997         }
3998 }
3999
4000 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4001         : Drag (e, i)
4002         , _selection_changed (false)
4003 {
4004         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4005         Gtk::Window* toplevel = _editor->current_toplevel();
4006         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4007
4008         assert (_marker);
4009
4010         _points.push_back (ArdourCanvas::Duple (0, 0));
4011
4012         _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4013 }
4014
4015 MarkerDrag::~MarkerDrag ()
4016 {
4017         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4018                 delete i->location;
4019         }
4020 }
4021
4022 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4023 {
4024         location = new Location (*l);
4025         markers.push_back (m);
4026         move_both = false;
4027 }
4028
4029 void
4030 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4031 {
4032         Drag::start_grab (event, cursor);
4033
4034         bool is_start;
4035
4036         Location *location = _editor->find_location_from_marker (_marker, is_start);
4037         _editor->_dragging_edit_point = true;
4038
4039         update_item (location);
4040
4041         // _drag_line->show();
4042         // _line->raise_to_top();
4043
4044         if (is_start) {
4045                 show_verbose_cursor_time (location->start());
4046         } else {
4047                 show_verbose_cursor_time (location->end());
4048         }
4049         setup_snap_delta (is_start ? location->start() : location->end());
4050
4051         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4052
4053         switch (op) {
4054         case Selection::Toggle:
4055                 /* we toggle on the button release */
4056                 break;
4057         case Selection::Set:
4058                 if (!_editor->selection->selected (_marker)) {
4059                         _editor->selection->set (_marker);
4060                         _selection_changed = true;
4061                 }
4062                 break;
4063         case Selection::Extend:
4064         {
4065                 Locations::LocationList ll;
4066                 list<ArdourMarker*> to_add;
4067                 framepos_t s, e;
4068                 _editor->selection->markers.range (s, e);
4069                 s = min (_marker->position(), s);
4070                 e = max (_marker->position(), e);
4071                 s = min (s, e);
4072                 e = max (s, e);
4073                 if (e < max_framepos) {
4074                         ++e;
4075                 }
4076                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4077                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4078                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4079                         if (lm) {
4080                                 if (lm->start) {
4081                                         to_add.push_back (lm->start);
4082                                 }
4083                                 if (lm->end) {
4084                                         to_add.push_back (lm->end);
4085                                 }
4086                         }
4087                 }
4088                 if (!to_add.empty()) {
4089                         _editor->selection->add (to_add);
4090                         _selection_changed = true;
4091                 }
4092                 break;
4093         }
4094         case Selection::Add:
4095                 _editor->selection->add (_marker);
4096                 _selection_changed = true;
4097
4098                 break;
4099         }
4100
4101         /* Set up copies for us to manipulate during the drag
4102          */
4103
4104         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4105
4106                 Location* l = _editor->find_location_from_marker (*i, is_start);
4107
4108                 if (!l) {
4109                         continue;
4110                 }
4111
4112                 if (l->is_mark()) {
4113                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4114                 } else {
4115                         /* range: check that the other end of the range isn't
4116                            already there.
4117                         */
4118                         CopiedLocationInfo::iterator x;
4119                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4120                                 if (*(*x).location == *l) {
4121                                         break;
4122                                 }
4123                         }
4124                         if (x == _copied_locations.end()) {
4125                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4126                         } else {
4127                                 (*x).markers.push_back (*i);
4128                                 (*x).move_both = true;
4129                         }
4130                 }
4131
4132         }
4133 }
4134
4135 void
4136 MarkerDrag::setup_pointer_frame_offset ()
4137 {
4138         bool is_start;
4139         Location *location = _editor->find_location_from_marker (_marker, is_start);
4140         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4141 }
4142
4143 void
4144 MarkerDrag::motion (GdkEvent* event, bool)
4145 {
4146         framecnt_t f_delta = 0;
4147         bool is_start;
4148         bool move_both = false;
4149         Location *real_location;
4150         Location *copy_location = 0;
4151         framecnt_t const sd = snap_delta (event->button.state);
4152
4153         framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4154         framepos_t next = newframe;
4155
4156         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4157                 move_both = true;
4158         }
4159
4160         CopiedLocationInfo::iterator x;
4161
4162         /* find the marker we're dragging, and compute the delta */
4163
4164         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4165
4166                 copy_location = (*x).location;
4167
4168                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4169
4170                         /* this marker is represented by this
4171                          * CopiedLocationMarkerInfo
4172                          */
4173
4174                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4175                                 /* que pasa ?? */
4176                                 return;
4177                         }
4178
4179                         if (real_location->is_mark()) {
4180                                 f_delta = newframe - copy_location->start();
4181                         } else {
4182
4183
4184                                 switch (_marker->type()) {
4185                                 case ArdourMarker::SessionStart:
4186                                 case ArdourMarker::RangeStart:
4187                                 case ArdourMarker::LoopStart:
4188                                 case ArdourMarker::PunchIn:
4189                                         f_delta = newframe - copy_location->start();
4190                                         break;
4191
4192                                 case ArdourMarker::SessionEnd:
4193                                 case ArdourMarker::RangeEnd:
4194                                 case ArdourMarker::LoopEnd:
4195                                 case ArdourMarker::PunchOut:
4196                                         f_delta = newframe - copy_location->end();
4197                                         break;
4198                                 default:
4199                                         /* what kind of marker is this ? */
4200                                         return;
4201                                 }
4202                         }
4203
4204                         break;
4205                 }
4206         }
4207
4208         if (x == _copied_locations.end()) {
4209                 /* hmm, impossible - we didn't find the dragged marker */
4210                 return;
4211         }
4212
4213         /* now move them all */
4214
4215         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4216
4217                 copy_location = x->location;
4218
4219                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4220                         continue;
4221                 }
4222
4223                 if (real_location->locked()) {
4224                         continue;
4225                 }
4226
4227                 if (copy_location->is_mark()) {
4228
4229                         /* now move it */
4230
4231                         copy_location->set_start (copy_location->start() + f_delta);
4232
4233                 } else {
4234
4235                         framepos_t new_start = copy_location->start() + f_delta;
4236                         framepos_t new_end = copy_location->end() + f_delta;
4237
4238                         if (is_start) { // start-of-range marker
4239
4240                                 if (move_both || (*x).move_both) {
4241                                         copy_location->set_start (new_start);
4242                                         copy_location->set_end (new_end);
4243                                 } else  if (new_start < copy_location->end()) {
4244                                         copy_location->set_start (new_start);
4245                                 } else if (newframe > 0) {
4246                                         //_editor->snap_to (next, RoundUpAlways, true);
4247                                         copy_location->set_end (next);
4248                                         copy_location->set_start (newframe);
4249                                 }
4250
4251                         } else { // end marker
4252
4253                                 if (move_both || (*x).move_both) {
4254                                         copy_location->set_end (new_end);
4255                                         copy_location->set_start (new_start);
4256                                 } else if (new_end > copy_location->start()) {
4257                                         copy_location->set_end (new_end);
4258                                 } else if (newframe > 0) {
4259                                         //_editor->snap_to (next, RoundDownAlways, true);
4260                                         copy_location->set_start (next);
4261                                         copy_location->set_end (newframe);
4262                                 }
4263                         }
4264                 }
4265
4266                 update_item (copy_location);
4267
4268                 /* now lookup the actual GUI items used to display this
4269                  * location and move them to wherever the copy of the location
4270                  * is now. This means that the logic in ARDOUR::Location is
4271                  * still enforced, even though we are not (yet) modifying
4272                  * the real Location itself.
4273                  */
4274
4275                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4276
4277                 if (lm) {
4278                         lm->set_position (copy_location->start(), copy_location->end());
4279                 }
4280
4281         }
4282
4283         assert (!_copied_locations.empty());
4284
4285         show_verbose_cursor_time (newframe);
4286 }
4287
4288 void
4289 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4290 {
4291         if (!movement_occurred) {
4292
4293                 if (was_double_click()) {
4294                         _editor->rename_marker (_marker);
4295                         return;
4296                 }
4297
4298                 /* just a click, do nothing but finish
4299                    off the selection process
4300                 */
4301
4302                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4303                 switch (op) {
4304                 case Selection::Set:
4305                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4306                                 _editor->selection->set (_marker);
4307                                 _selection_changed = true;
4308                         }
4309                         break;
4310
4311                 case Selection::Toggle:
4312                         /* we toggle on the button release, click only */
4313                         _editor->selection->toggle (_marker);
4314                         _selection_changed = true;
4315
4316                         break;
4317
4318                 case Selection::Extend:
4319                 case Selection::Add:
4320                         break;
4321                 }
4322
4323                 if (_selection_changed) {
4324                         _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4325                         _editor->commit_reversible_selection_op();
4326                 }
4327
4328                 return;
4329         }
4330
4331         _editor->_dragging_edit_point = false;
4332
4333         XMLNode &before = _editor->session()->locations()->get_state();
4334         bool in_command = false;
4335
4336         MarkerSelection::iterator i;
4337         CopiedLocationInfo::iterator x;
4338         bool is_start;
4339
4340         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4341              x != _copied_locations.end() && i != _editor->selection->markers.end();
4342              ++i, ++x) {
4343
4344                 Location * location = _editor->find_location_from_marker (*i, is_start);
4345
4346                 if (location) {
4347
4348                         if (location->locked()) {
4349                                 continue;
4350                         }
4351                         if (!in_command) {
4352                                 _editor->begin_reversible_command ( _("move marker") );
4353                                 in_command = true;
4354                         }
4355                         if (location->is_mark()) {
4356                                 location->set_start (((*x).location)->start());
4357                         } else {
4358                                 location->set (((*x).location)->start(), ((*x).location)->end());
4359                         }
4360
4361                         if (location->is_session_range()) {
4362                                 _editor->session()->set_end_is_free (false);
4363                         }
4364                 }
4365         }
4366
4367         if (in_command) {
4368                 XMLNode &after = _editor->session()->locations()->get_state();
4369                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4370                 _editor->commit_reversible_command ();
4371         }
4372 }
4373
4374 void
4375 MarkerDrag::aborted (bool movement_occurred)
4376 {
4377         if (!movement_occurred) {
4378                 return;
4379         }
4380
4381         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4382
4383                 /* move all markers to their original location */
4384
4385
4386                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4387
4388                         bool is_start;
4389                         Location * location = _editor->find_location_from_marker (*m, is_start);
4390
4391                         if (location) {
4392                                 (*m)->set_position (is_start ? location->start() : location->end());
4393                         }
4394                 }
4395         }
4396 }
4397
4398 void
4399 MarkerDrag::update_item (Location*)
4400 {
4401         /* noop */
4402 }
4403
4404 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4405         : Drag (e, i)
4406         , _fixed_grab_x (0.0)
4407         , _fixed_grab_y (0.0)
4408         , _cumulative_x_drag (0.0)
4409         , _cumulative_y_drag (0.0)
4410         , _pushing (false)
4411         , _final_index (0)
4412 {
4413         if (_zero_gain_fraction < 0.0) {
4414                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4415         }
4416
4417         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4418
4419         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4420         assert (_point);
4421 }
4422
4423
4424 void
4425 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4426 {
4427         Drag::start_grab (event, _editor->cursors()->fader);
4428
4429         // start the grab at the center of the control point so
4430         // the point doesn't 'jump' to the mouse after the first drag
4431         _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4432         _fixed_grab_y = _point->get_y();
4433
4434         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4435         setup_snap_delta (pos);
4436
4437         float const fraction = 1 - (_point->get_y() / _point->line().height());
4438         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4439
4440         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4441
4442         if (!_point->can_slide ()) {
4443                 _x_constrained = true;
4444         }
4445 }
4446
4447 void
4448 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4449 {
4450         double dx = _drags->current_pointer_x() - last_pointer_x();
4451         double dy = current_pointer_y() - last_pointer_y();
4452         bool need_snap = true;
4453
4454         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4455                 dx *= 0.1;
4456                 dy *= 0.1;
4457                 need_snap = false;
4458         }
4459
4460         /* coordinate in pixels relative to the start of the region (for region-based automation)
4461            or track (for track-based automation) */
4462         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4463         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4464
4465         // calculate zero crossing point. back off by .01 to stay on the
4466         // positive side of zero
4467         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4468
4469         if (_x_constrained) {
4470                 cx = _fixed_grab_x;
4471         }
4472         if (_y_constrained) {
4473                 cy = _fixed_grab_y;
4474         }
4475
4476         _cumulative_x_drag = cx - _fixed_grab_x;
4477         _cumulative_y_drag = cy - _fixed_grab_y;
4478
4479         cx = max (0.0, cx);
4480         cy = max (0.0, cy);
4481         cy = min ((double) _point->line().height(), cy);
4482
4483         // make sure we hit zero when passing through
4484         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4485                 cy = zero_gain_y;
4486         }
4487
4488         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4489         if (!_x_constrained && need_snap) {
4490                 _editor->snap_to_with_modifier (cx_frames, event);
4491         }
4492
4493         cx_frames -= snap_delta (event->button.state);
4494         cx_frames = min (cx_frames, _point->line().maximum_time() + _point->line().offset());
4495
4496         float const fraction = 1.0 - (cy / _point->line().height());
4497
4498         if (first_motion) {
4499                 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4500                 _editor->begin_reversible_command (_("automation event move"));
4501                 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4502         }
4503         pair<double, float> result;
4504         result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4505
4506         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4507 }
4508
4509 void
4510 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4511 {
4512         if (!movement_occurred) {
4513
4514                 /* just a click */
4515                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4516                         _editor->reset_point_selection ();
4517                 }
4518
4519         } else {
4520                 _point->line().end_drag (_pushing, _final_index);
4521                 _editor->commit_reversible_command ();
4522         }
4523 }
4524
4525 void
4526 ControlPointDrag::aborted (bool)
4527 {
4528         _point->line().reset ();
4529 }
4530
4531 bool
4532 ControlPointDrag::active (Editing::MouseMode m)
4533 {
4534         if (m == Editing::MouseDraw) {
4535                 /* always active in mouse draw */
4536                 return true;
4537         }
4538
4539         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4540         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4541 }
4542
4543 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4544         : Drag (e, i)
4545         , _line (0)
4546         , _fixed_grab_x (0.0)
4547         , _fixed_grab_y (0.0)
4548         , _cumulative_y_drag (0)
4549         , _before (0)
4550         , _after (0)
4551 {
4552         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4553 }
4554
4555 void
4556 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4557 {
4558         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4559         assert (_line);
4560
4561         _item = &_line->grab_item ();
4562
4563         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4564            origin, and ditto for y.
4565         */
4566
4567         double mx = event->button.x;
4568         double my = event->button.y;
4569
4570         _line->grab_item().canvas_to_item (mx, my);
4571
4572         framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4573
4574         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4575                 /* no adjacent points */
4576                 return;
4577         }
4578
4579         Drag::start_grab (event, _editor->cursors()->fader);
4580
4581         /* store grab start in item frame */
4582         double const bx = _line->nth (_before)->get_x();
4583         double const ax = _line->nth (_after)->get_x();
4584         double const click_ratio = (ax - mx) / (ax - bx);
4585
4586         double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4587
4588         _fixed_grab_x = mx;
4589         _fixed_grab_y = cy;
4590
4591         double fraction = 1.0 - (cy / _line->height());
4592
4593         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4594 }
4595
4596 void
4597 LineDrag::motion (GdkEvent* event, bool first_move)
4598 {
4599         double dy = current_pointer_y() - last_pointer_y();
4600
4601         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4602                 dy *= 0.1;
4603         }
4604
4605         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4606
4607         _cumulative_y_drag = cy - _fixed_grab_y;
4608
4609         cy = max (0.0, cy);
4610         cy = min ((double) _line->height(), cy);
4611
4612         double const fraction = 1.0 - (cy / _line->height());
4613         uint32_t ignored;
4614
4615         if (first_move) {
4616                 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4617
4618                 _editor->begin_reversible_command (_("automation range move"));
4619                 _line->start_drag_line (_before, _after, initial_fraction);
4620         }
4621
4622         /* we are ignoring x position for this drag, so we can just pass in anything */
4623         pair<double, float> result;
4624
4625         result = _line->drag_motion (0, fraction, true, false, ignored);
4626         show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4627 }
4628
4629 void
4630 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4631 {
4632         if (movement_occurred) {
4633                 motion (event, false);
4634                 _line->end_drag (false, 0);
4635                 _editor->commit_reversible_command ();
4636         } else {
4637                 /* add a new control point on the line */
4638
4639                 AutomationTimeAxisView* atv;
4640
4641                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4642                         framepos_t where = grab_frame ();
4643
4644                         double cx = 0;
4645                         double cy = _fixed_grab_y;
4646
4647                         _line->grab_item().item_to_canvas (cx, cy);
4648
4649                         atv->add_automation_event (event, where, cy, false);
4650                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4651                         AudioRegionView* arv;
4652
4653                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4654                                 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4655                         }
4656                 }
4657         }
4658 }
4659
4660 void
4661 LineDrag::aborted (bool)
4662 {
4663         _line->reset ();
4664 }
4665
4666 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4667         : Drag (e, i),
4668           _line (0),
4669           _arv (0),
4670           _region_view_grab_x (0.0),
4671           _cumulative_x_drag (0),
4672           _before (0.0),
4673           _max_x (0)
4674 {
4675         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4676 }
4677
4678 void
4679 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4680 {
4681         Drag::start_grab (event);
4682
4683         _line = reinterpret_cast<Line*> (_item);
4684         assert (_line);
4685
4686         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4687
4688         double cx = event->button.x;
4689         double cy = event->button.y;
4690
4691         _item->parent()->canvas_to_item (cx, cy);
4692
4693         /* store grab start in parent frame */
4694         _region_view_grab_x = cx;
4695
4696         _before = *(float*) _item->get_data ("position");
4697
4698         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4699
4700         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4701 }
4702
4703 void
4704 FeatureLineDrag::motion (GdkEvent*, bool)
4705 {
4706         double dx = _drags->current_pointer_x() - last_pointer_x();
4707
4708         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4709
4710         _cumulative_x_drag += dx;
4711
4712         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4713
4714         if (cx > _max_x){
4715                 cx = _max_x;
4716         }
4717         else if(cx < 0){
4718                 cx = 0;
4719         }
4720
4721         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4722         assert (bbox);
4723         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4724
4725         float *pos = new float;
4726         *pos = cx;
4727
4728         _line->set_data ("position", pos);
4729
4730         _before = cx;
4731 }
4732
4733 void
4734 FeatureLineDrag::finished (GdkEvent*, bool)
4735 {
4736         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4737         _arv->update_transient(_before, _before);
4738 }
4739
4740 void
4741 FeatureLineDrag::aborted (bool)
4742 {
4743         //_line->reset ();
4744 }
4745
4746 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4747         : Drag (e, i)
4748         , _vertical_only (false)
4749 {
4750         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4751 }
4752
4753 void
4754 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4755 {
4756         Drag::start_grab (event);
4757         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4758 }
4759
4760 void
4761 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4762 {
4763         framepos_t start;
4764         framepos_t end;
4765         double y1;
4766         double y2;
4767
4768         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4769
4770         framepos_t grab = grab_frame ();
4771         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4772                 _editor->snap_to_with_modifier (grab, event);
4773         } else {
4774                 grab = raw_grab_frame ();
4775         }
4776
4777         /* base start and end on initial click position */
4778
4779         if (pf < grab) {
4780                 start = pf;
4781                 end = grab;
4782         } else {
4783                 end = pf;
4784                 start = grab;
4785         }
4786
4787         if (current_pointer_y() < grab_y()) {
4788                 y1 = current_pointer_y();
4789                 y2 = grab_y();
4790         } else {
4791                 y2 = current_pointer_y();
4792                 y1 = grab_y();
4793         }
4794
4795         if (start != end || y1 != y2) {
4796
4797                 double x1 = _editor->sample_to_pixel (start);
4798                 double x2 = _editor->sample_to_pixel (end);
4799                 const double min_dimension = 2.0;
4800
4801                 if (_vertical_only) {
4802                         /* fixed 10 pixel width */
4803                         x2 = x1 + 10;
4804                 } else {
4805                         if (x2 < x1) {
4806                                 x2 = min (x1 - min_dimension, x2);
4807                         } else {
4808                                 x2 = max (x1 + min_dimension, x2);
4809                         }
4810                 }
4811
4812                 if (y2 < y1) {
4813                         y2 = min (y1 - min_dimension, y2);
4814                 } else {
4815                         y2 = max (y1 + min_dimension, y2);
4816                 }
4817
4818                 /* translate rect into item space and set */
4819
4820                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4821
4822                 /* this drag is a _trackview_only == true drag, so the y1 and
4823                  * y2 (computed using current_pointer_y() and grab_y()) will be
4824                  * relative to the top of the trackview group). The
4825                  * rubberband rect has the same parent/scroll offset as the
4826                  * the trackview group, so we can use the "r" rect directly
4827                  * to set the shape of the rubberband.
4828                  */
4829
4830                 _editor->rubberband_rect->set (r);
4831                 _editor->rubberband_rect->show();
4832                 _editor->rubberband_rect->raise_to_top();
4833
4834                 show_verbose_cursor_time (pf);
4835
4836                 do_select_things (event, true);
4837         }
4838 }
4839
4840 void
4841 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4842 {
4843         framepos_t x1;
4844         framepos_t x2;
4845         framepos_t grab = grab_frame ();
4846         framepos_t lpf = last_pointer_frame ();
4847
4848         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4849                 grab = raw_grab_frame ();
4850                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4851         }
4852
4853         if (grab < lpf) {
4854                 x1 = grab;
4855                 x2 = lpf;
4856         } else {
4857                 x2 = grab;
4858                 x1 = lpf;
4859         }
4860
4861         double y1;
4862         double y2;
4863
4864         if (current_pointer_y() < grab_y()) {
4865                 y1 = current_pointer_y();
4866                 y2 = grab_y();
4867         } else {
4868                 y2 = current_pointer_y();
4869                 y1 = grab_y();
4870         }
4871
4872         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4873 }
4874
4875 void
4876 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4877 {
4878         if (movement_occurred) {
4879
4880                 motion (event, false);
4881                 do_select_things (event, false);
4882
4883         } else {
4884
4885                 /* just a click */
4886
4887                 bool do_deselect = true;
4888                 MidiTimeAxisView* mtv;
4889
4890                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4891                         /* MIDI track */
4892                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4893                                 /* nothing selected */
4894                                 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4895                                 do_deselect = false;
4896                         }
4897                 }
4898
4899                 /* do not deselect if Primary or Tertiary (toggle-select or
4900                  * extend-select are pressed.
4901                  */
4902
4903                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4904                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4905                     do_deselect) {
4906                         deselect_things ();
4907                 }
4908
4909         }
4910
4911         _editor->rubberband_rect->hide();
4912 }
4913
4914 void
4915 RubberbandSelectDrag::aborted (bool)
4916 {
4917         _editor->rubberband_rect->hide ();
4918 }
4919
4920 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4921         : RegionDrag (e, i, p, v)
4922 {
4923         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4924 }
4925
4926 void
4927 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4928 {
4929         Drag::start_grab (event, cursor);
4930
4931         _editor->get_selection().add (_primary);
4932
4933         framepos_t where = _primary->region()->position();
4934         setup_snap_delta (where);
4935
4936         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4937 }
4938
4939 void
4940 TimeFXDrag::motion (GdkEvent* event, bool)
4941 {
4942         RegionView* rv = _primary;
4943         StreamView* cv = rv->get_time_axis_view().view ();
4944
4945         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4946         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4947         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4948         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4949         _editor->snap_to_with_modifier (pf, event);
4950         pf -= snap_delta (event->button.state);
4951
4952         if (pf > rv->region()->position()) {
4953                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4954         }
4955
4956         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4957 }
4958
4959 void
4960 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4961 {
4962         /* this may have been a single click, no drag. We still want the dialog
4963            to show up in that case, so that the user can manually edit the
4964            parameters for the timestretch.
4965         */
4966
4967         float fraction = 1.0;
4968
4969         if (movement_occurred) {
4970
4971                 motion (event, false);
4972
4973                 _primary->get_time_axis_view().hide_timestretch ();
4974
4975                 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4976
4977                 if (adjusted_frame_pos < _primary->region()->position()) {
4978                         /* backwards drag of the left edge - not usable */
4979                         return;
4980                 }
4981
4982                 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4983
4984                 fraction = (double) newlen / (double) _primary->region()->length();
4985
4986 #ifndef USE_RUBBERBAND
4987                 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4988                 if (_primary->region()->data_type() == DataType::AUDIO) {
4989                         fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4990                 }
4991 #endif
4992         }
4993
4994         if (!_editor->get_selection().regions.empty()) {
4995                 /* primary will already be included in the selection, and edit
4996                    group shared editing will propagate selection across
4997                    equivalent regions, so just use the current region
4998                    selection.
4999                 */
5000
5001                 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5002                         error << _("An error occurred while executing time stretch operation") << endmsg;
5003                 }
5004         }
5005 }
5006
5007 void
5008 TimeFXDrag::aborted (bool)
5009 {
5010         _primary->get_time_axis_view().hide_timestretch ();
5011 }
5012
5013 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5014         : Drag (e, i)
5015 {
5016         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5017 }
5018
5019 void
5020 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5021 {
5022         Drag::start_grab (event);
5023 }
5024
5025 void
5026 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5027 {
5028         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5029 }
5030
5031 void
5032 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5033 {
5034         if (movement_occurred && _editor->session()) {
5035                 /* make sure we stop */
5036                 _editor->session()->request_transport_speed (0.0);
5037         }
5038 }
5039
5040 void
5041 ScrubDrag::aborted (bool)
5042 {
5043         /* XXX: TODO */
5044 }
5045
5046 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5047         : Drag (e, i)
5048         , _operation (o)
5049         , _add (false)
5050         , _time_selection_at_start (!_editor->get_selection().time.empty())
5051 {
5052         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5053
5054         if (_time_selection_at_start) {
5055                 start_at_start = _editor->get_selection().time.start();
5056                 end_at_start = _editor->get_selection().time.end_frame();
5057         }
5058 }
5059
5060 void
5061 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5062 {
5063         if (_editor->session() == 0) {
5064                 return;
5065         }
5066
5067         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5068
5069         switch (_operation) {
5070         case CreateSelection:
5071                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5072                         _add = true;
5073                 } else {
5074                         _add = false;
5075                 }
5076                 cursor = _editor->cursors()->selector;
5077                 Drag::start_grab (event, cursor);
5078                 break;
5079
5080         case SelectionStartTrim:
5081                 if (_editor->clicked_axisview) {
5082                         _editor->clicked_axisview->order_selection_trims (_item, true);
5083                 }
5084                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5085                 break;
5086
5087         case SelectionEndTrim:
5088                 if (_editor->clicked_axisview) {
5089                         _editor->clicked_axisview->order_selection_trims (_item, false);
5090                 }
5091                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5092                 break;
5093
5094         case SelectionMove:
5095                 Drag::start_grab (event, cursor);
5096                 break;
5097
5098         case SelectionExtend:
5099                 Drag::start_grab (event, cursor);
5100                 break;
5101         }
5102
5103         if (_operation == SelectionMove) {
5104                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5105         } else {
5106                 show_verbose_cursor_time (adjusted_current_frame (event));
5107         }
5108 }
5109
5110 void
5111 SelectionDrag::setup_pointer_frame_offset ()
5112 {
5113         switch (_operation) {
5114         case CreateSelection:
5115                 _pointer_frame_offset = 0;
5116                 break;
5117
5118         case SelectionStartTrim:
5119         case SelectionMove:
5120                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5121                 break;
5122
5123         case SelectionEndTrim:
5124                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5125                 break;
5126
5127         case SelectionExtend:
5128                 break;
5129         }
5130 }
5131
5132 void
5133 SelectionDrag::motion (GdkEvent* event, bool first_move)
5134 {
5135         framepos_t start = 0;
5136         framepos_t end = 0;
5137         framecnt_t length = 0;
5138         framecnt_t distance = 0;
5139
5140         framepos_t const pending_position = adjusted_current_frame (event);
5141
5142         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5143                 return;
5144         }
5145
5146         switch (_operation) {
5147         case CreateSelection:
5148         {
5149                 framepos_t grab = grab_frame ();
5150
5151                 if (first_move) {
5152                         grab = adjusted_current_frame (event, false);
5153                         if (grab < pending_position) {
5154                                 _editor->snap_to (grab, RoundDownMaybe);
5155                         }  else {
5156                                 _editor->snap_to (grab, RoundUpMaybe);
5157                         }
5158                 }
5159
5160                 if (pending_position < grab) {
5161                         start = pending_position;
5162                         end = grab;
5163                 } else {
5164                         end = pending_position;
5165                         start = grab;
5166                 }
5167
5168                 /* first drag: Either add to the selection
5169                    or create a new selection
5170                 */
5171
5172                 if (first_move) {
5173
5174                         if (_add) {
5175
5176                                 /* adding to the selection */
5177                                 _editor->set_selected_track_as_side_effect (Selection::Add);
5178                                 _editor->clicked_selection = _editor->selection->add (start, end);
5179                                 _add = false;
5180
5181                         } else {
5182
5183                                 /* new selection */
5184
5185                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5186                                         _editor->set_selected_track_as_side_effect (Selection::Set);
5187                                 }
5188
5189                                 _editor->clicked_selection = _editor->selection->set (start, end);
5190                         }
5191                 }
5192
5193                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5194                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5195                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5196                 if (atest) {
5197                         _editor->selection->add (atest);
5198                         break;
5199                 }
5200
5201                 /* select all tracks within the rectangle that we've marked out so far */
5202                 TrackViewList new_selection;
5203                 TrackViewList& all_tracks (_editor->track_views);
5204
5205                 ArdourCanvas::Coord const top = grab_y();
5206                 ArdourCanvas::Coord const bottom = current_pointer_y();
5207
5208                 if (top >= 0 && bottom >= 0) {
5209
5210                         //first, find the tracks that are covered in the y range selection
5211                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5212                                 if ((*i)->covered_by_y_range (top, bottom)) {
5213                                         new_selection.push_back (*i);
5214                                 }
5215                         }
5216
5217                         //now find any tracks that are GROUPED with the tracks we selected
5218                         TrackViewList grouped_add = new_selection;
5219                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5220                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5221                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5222                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5223                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5224                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5225                                                         grouped_add.push_back (*j);
5226                                         }
5227                                 }
5228                         }
5229
5230                         //now compare our list with the current selection, and add or remove as necessary
5231                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5232                         TrackViewList tracks_to_add;
5233                         TrackViewList tracks_to_remove;
5234                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5235                                 if ( !_editor->selection->tracks.contains ( *i ) )
5236                                         tracks_to_add.push_back ( *i );
5237                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5238                                 if ( !grouped_add.contains ( *i ) )
5239                                         tracks_to_remove.push_back ( *i );
5240                         _editor->selection->add(tracks_to_add);
5241                         _editor->selection->remove(tracks_to_remove);
5242
5243                 }
5244         }
5245         break;
5246
5247         case SelectionStartTrim:
5248
5249                 end = _editor->selection->time[_editor->clicked_selection].end;
5250
5251                 if (pending_position > end) {
5252                         start = end;
5253                 } else {
5254                         start = pending_position;
5255                 }
5256                 break;
5257
5258         case SelectionEndTrim:
5259
5260                 start = _editor->selection->time[_editor->clicked_selection].start;
5261
5262                 if (pending_position < start) {
5263                         end = start;
5264                 } else {
5265                         end = pending_position;
5266                 }
5267
5268                 break;
5269
5270         case SelectionMove:
5271
5272                 start = _editor->selection->time[_editor->clicked_selection].start;
5273                 end = _editor->selection->time[_editor->clicked_selection].end;
5274
5275                 length = end - start;
5276                 distance = pending_position - start;
5277                 start = pending_position;
5278                 _editor->snap_to (start);
5279
5280                 end = start + length;
5281
5282                 break;
5283
5284         case SelectionExtend:
5285                 break;
5286         }
5287
5288         if (start != end) {
5289                 switch (_operation) {
5290                 case SelectionMove:
5291                         if (_time_selection_at_start) {
5292                                 _editor->selection->move_time (distance);
5293                         }
5294                         break;
5295                 default:
5296                         _editor->selection->replace (_editor->clicked_selection, start, end);
5297                 }
5298         }
5299
5300         if (_operation == SelectionMove) {
5301                 show_verbose_cursor_time(start);
5302         } else {
5303                 show_verbose_cursor_time(pending_position);
5304         }
5305 }
5306
5307 void
5308 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5309 {
5310         Session* s = _editor->session();
5311
5312         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5313         if (movement_occurred) {
5314                 motion (event, false);
5315                 /* XXX this is not object-oriented programming at all. ick */
5316                 if (_editor->selection->time.consolidate()) {
5317                         _editor->selection->TimeChanged ();
5318                 }
5319
5320                 /* XXX what if its a music time selection? */
5321                 if (s) {
5322                         if (s->get_play_range() && s->transport_rolling()) {
5323                                 s->request_play_range (&_editor->selection->time, true);
5324                         } else if (!s->config.get_external_sync()) {
5325                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5326                                         if (_operation == SelectionEndTrim)
5327                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5328                                         else
5329                                                 s->request_locate (_editor->get_selection().time.start());
5330                                 }
5331                         }
5332
5333                         if (_editor->get_selection().time.length() != 0) {
5334                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5335                         } else {
5336                                 s->clear_range_selection ();
5337                         }
5338                 }
5339
5340         } else {
5341                 /* just a click, no pointer movement.
5342                  */
5343
5344                 if (was_double_click()) {
5345                         if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5346                                 _editor->temporal_zoom_selection (Both);
5347                                 return;
5348                         }
5349                 }
5350
5351                 if (_operation == SelectionExtend) {
5352                         if (_time_selection_at_start) {
5353                                 framepos_t pos = adjusted_current_frame (event, false);
5354                                 framepos_t start = min (pos, start_at_start);
5355                                 framepos_t end = max (pos, end_at_start);
5356                                 _editor->selection->set (start, end);
5357                         }
5358                 } else {
5359                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5360                                 if (_editor->clicked_selection) {
5361                                         _editor->selection->remove (_editor->clicked_selection);
5362                                 }
5363                         } else {
5364                                 if (!_editor->clicked_selection) {
5365                                         _editor->selection->clear_time();
5366                                 }
5367                         }
5368                 }
5369
5370                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5371                         _editor->selection->set (_editor->clicked_axisview);
5372                 }
5373
5374                 if (s && s->get_play_range () && s->transport_rolling()) {
5375                         s->request_stop (false, false);
5376                 }
5377
5378         }
5379
5380         _editor->stop_canvas_autoscroll ();
5381         _editor->clicked_selection = 0;
5382         _editor->commit_reversible_selection_op ();
5383 }
5384
5385 void
5386 SelectionDrag::aborted (bool)
5387 {
5388         /* XXX: TODO */
5389 }
5390
5391 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5392         : Drag (e, i, false),
5393           _operation (o),
5394           _copy (false)
5395 {
5396         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5397
5398         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5399                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5400                                                                       physical_screen_height (_editor->current_toplevel()->get_window())));
5401         _drag_rect->hide ();
5402
5403         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5404         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5405 }
5406
5407 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5408 {
5409         /* normal canvas items will be cleaned up when their parent group is deleted. But
5410            this item is created as the child of a long-lived parent group, and so we
5411            need to explicitly delete it.
5412         */
5413         delete _drag_rect;
5414 }
5415
5416 void
5417 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5418 {
5419         if (_editor->session() == 0) {
5420                 return;
5421         }
5422
5423         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5424
5425         if (!_editor->temp_location) {
5426                 _editor->temp_location = new Location (*_editor->session());
5427         }
5428
5429         switch (_operation) {
5430         case CreateSkipMarker:
5431         case CreateRangeMarker:
5432         case CreateTransportMarker:
5433         case CreateCDMarker:
5434
5435                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5436                         _copy = true;
5437                 } else {
5438                         _copy = false;
5439                 }
5440                 cursor = _editor->cursors()->selector;
5441                 break;
5442         }
5443
5444         Drag::start_grab (event, cursor);
5445
5446         show_verbose_cursor_time (adjusted_current_frame (event));
5447 }
5448
5449 void
5450 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5451 {
5452         framepos_t start = 0;
5453         framepos_t end = 0;
5454         ArdourCanvas::Rectangle *crect;
5455
5456         switch (_operation) {
5457         case CreateSkipMarker:
5458                 crect = _editor->range_bar_drag_rect;
5459                 break;
5460         case CreateRangeMarker:
5461                 crect = _editor->range_bar_drag_rect;
5462                 break;
5463         case CreateTransportMarker:
5464                 crect = _editor->transport_bar_drag_rect;
5465                 break;
5466         case CreateCDMarker:
5467                 crect = _editor->cd_marker_bar_drag_rect;
5468                 break;
5469         default:
5470                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5471                 return;
5472                 break;
5473         }
5474
5475         framepos_t const pf = adjusted_current_frame (event);
5476
5477         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5478                 framepos_t grab = grab_frame ();
5479                 _editor->snap_to (grab);
5480
5481                 if (pf < grab_frame()) {
5482                         start = pf;
5483                         end = grab;
5484                 } else {
5485                         end = pf;
5486                         start = grab;
5487                 }
5488
5489                 /* first drag: Either add to the selection
5490                    or create a new selection.
5491                 */
5492
5493                 if (first_move) {
5494
5495                         _editor->temp_location->set (start, end);
5496
5497                         crect->show ();
5498
5499                         update_item (_editor->temp_location);
5500                         _drag_rect->show();
5501                         //_drag_rect->raise_to_top();
5502
5503                 }
5504         }
5505
5506         if (start != end) {
5507                 _editor->temp_location->set (start, end);
5508
5509                 double x1 = _editor->sample_to_pixel (start);
5510                 double x2 = _editor->sample_to_pixel (end);
5511                 crect->set_x0 (x1);
5512                 crect->set_x1 (x2);
5513
5514                 update_item (_editor->temp_location);
5515         }
5516
5517         show_verbose_cursor_time (pf);
5518
5519 }
5520
5521 void
5522 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5523 {
5524         Location * newloc = 0;
5525         string rangename;
5526         int flags;
5527
5528         if (movement_occurred) {
5529                 motion (event, false);
5530                 _drag_rect->hide();
5531
5532                 switch (_operation) {
5533                 case CreateSkipMarker:
5534                 case CreateRangeMarker:
5535                 case CreateCDMarker:
5536                     {
5537                         XMLNode &before = _editor->session()->locations()->get_state();
5538                         if (_operation == CreateSkipMarker) {
5539                                 _editor->begin_reversible_command (_("new skip marker"));
5540                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5541                                 flags = Location::IsRangeMarker | Location::IsSkip;
5542                                 _editor->range_bar_drag_rect->hide();
5543                         } else if (_operation == CreateCDMarker) {
5544                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5545                                 _editor->begin_reversible_command (_("new CD marker"));
5546                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5547                                 _editor->cd_marker_bar_drag_rect->hide();
5548                         } else {
5549                                 _editor->begin_reversible_command (_("new skip marker"));
5550                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5551                                 flags = Location::IsRangeMarker;
5552                                 _editor->range_bar_drag_rect->hide();
5553                         }
5554                         newloc = new Location (
5555                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5556                                 );
5557
5558                         _editor->session()->locations()->add (newloc, true);
5559                         XMLNode &after = _editor->session()->locations()->get_state();
5560                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5561                         _editor->commit_reversible_command ();
5562                         break;
5563                     }
5564
5565                 case CreateTransportMarker:
5566                         // popup menu to pick loop or punch
5567                         _editor->new_transport_marker_context_menu (&event->button, _item);
5568                         break;
5569                 }
5570
5571         } else {
5572
5573                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5574
5575                 if (_operation == CreateTransportMarker) {
5576
5577                         /* didn't drag, so just locate */
5578
5579                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5580
5581                 } else if (_operation == CreateCDMarker) {
5582
5583                         /* didn't drag, but mark is already created so do
5584                          * nothing */
5585
5586                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5587
5588                         framepos_t start;
5589                         framepos_t end;
5590
5591                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5592
5593                         if (end == max_framepos) {
5594                                 end = _editor->session()->current_end_frame ();
5595                         }
5596
5597                         if (start == max_framepos) {
5598                                 start = _editor->session()->current_start_frame ();
5599                         }
5600
5601                         switch (_editor->mouse_mode) {
5602                         case MouseObject:
5603                                 /* find the two markers on either side and then make the selection from it */
5604                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5605                                 break;
5606
5607                         case MouseRange:
5608                                 /* find the two markers on either side of the click and make the range out of it */
5609                                 _editor->selection->set (start, end);
5610                                 break;
5611
5612                         default:
5613                                 break;
5614                         }
5615                 }
5616         }
5617
5618         _editor->stop_canvas_autoscroll ();
5619 }
5620
5621 void
5622 RangeMarkerBarDrag::aborted (bool movement_occurred)
5623 {
5624         if (movement_occurred) {
5625                 _drag_rect->hide ();
5626         }
5627 }
5628
5629 void
5630 RangeMarkerBarDrag::update_item (Location* location)
5631 {
5632         double const x1 = _editor->sample_to_pixel (location->start());
5633         double const x2 = _editor->sample_to_pixel (location->end());
5634
5635         _drag_rect->set_x0 (x1);
5636         _drag_rect->set_x1 (x2);
5637 }
5638
5639 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5640         : Drag (e, i)
5641         , _cumulative_dx (0)
5642         , _cumulative_dy (0)
5643         , _was_selected (false)
5644 {
5645         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5646
5647         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5648         assert (_primary);
5649         _region = &_primary->region_view ();
5650         _note_height = _region->midi_stream_view()->note_height ();
5651 }
5652
5653 void
5654 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5655 {
5656         Drag::start_grab (event);
5657         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5658
5659         if (!(_was_selected = _primary->selected())) {
5660
5661                 /* tertiary-click means extend selection - we'll do that on button release,
5662                    so don't add it here, because otherwise we make it hard to figure
5663                    out the "extend-to" range.
5664                 */
5665
5666                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5667
5668                 if (!extend) {
5669                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5670
5671                         if (add) {
5672                                 _region->note_selected (_primary, true);
5673                         } else {
5674                                 _editor->get_selection().clear_points();
5675                                 _region->unique_select (_primary);
5676                         }
5677                 }
5678         }
5679 }
5680
5681 /** @return Current total drag x change in frames */
5682 frameoffset_t
5683 NoteDrag::total_dx (const guint state) const
5684 {
5685         if (_x_constrained) {
5686                 return 0;
5687         }
5688         TempoMap& map (_editor->session()->tempo_map());
5689
5690         /* dx in frames */
5691         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5692
5693         /* primary note time */
5694         double const quarter_note_start = _region->region()->quarter_note() - _region->midi_region()->start_beats();
5695         frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5696
5697         /* new time of the primary note in session frames */
5698         frameoffset_t st = n + dx + snap_delta (state);
5699
5700         framepos_t const rp = _region->region()->position ();
5701
5702         /* prevent the note being dragged earlier than the region's position */
5703         st = max (st, rp);
5704
5705         /* possibly snap and return corresponding delta */
5706
5707         bool snap = true;
5708
5709         if (ArdourKeyboard::indicates_snap (state)) {
5710                 if (_editor->snap_mode () != SnapOff) {
5711                         snap = false;
5712                 }
5713         } else {
5714                 if (_editor->snap_mode () == SnapOff) {
5715                         snap = false;
5716                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5717                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5718                                 snap = true;
5719                         }
5720                 }
5721         }
5722
5723         frameoffset_t ret;
5724         if (snap) {
5725                 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5726                 ret =  _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5727         } else {
5728                 ret = st - n - snap_delta (state);
5729         }
5730         return ret;
5731 }
5732
5733 /** @return Current total drag y change in note number */
5734 int8_t
5735 NoteDrag::total_dy () const
5736 {
5737         if (_y_constrained) {
5738                 return 0;
5739         }
5740
5741         double const y = _region->midi_view()->y_position ();
5742         /* new current note */
5743         uint8_t n = _region->y_to_note (current_pointer_y () - y);
5744         /* clamp */
5745         MidiStreamView* msv = _region->midi_stream_view ();
5746         n = max (msv->lowest_note(), n);
5747         n = min (msv->highest_note(), n);
5748         /* and work out delta */
5749         return n - _region->y_to_note (grab_y() - y);
5750 }
5751
5752 void
5753 NoteDrag::motion (GdkEvent * event, bool)
5754 {
5755         /* Total change in x and y since the start of the drag */
5756         frameoffset_t const dx = total_dx (event->button.state);
5757         int8_t const dy = total_dy ();
5758
5759         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5760         double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5761         double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5762
5763         if (tdx || tdy) {
5764                 _cumulative_dx += tdx;
5765                 _cumulative_dy += tdy;
5766
5767                 int8_t note_delta = total_dy();
5768
5769                 if (tdx || tdy) {
5770                         _region->move_selection (tdx, tdy, note_delta);
5771
5772                         /* the new note value may be the same as the old one, but we
5773                          * don't know what that means because the selection may have
5774                          * involved more than one note and we might be doing something
5775                          * odd with them. so show the note value anyway, always.
5776                          */
5777
5778                         uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5779
5780                         _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5781                 }
5782         }
5783 }
5784
5785 void
5786 NoteDrag::finished (GdkEvent* ev, bool moved)
5787 {
5788         if (!moved) {
5789                 /* no motion - select note */
5790
5791                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5792                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5793
5794                         bool changed = false;
5795
5796                         if (_was_selected) {
5797                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5798                                 if (add) {
5799                                         _region->note_deselected (_primary);
5800                                         changed = true;
5801                                 } else {
5802                                         _editor->get_selection().clear_points();
5803                                         _region->unique_select (_primary);
5804                                         changed = true;
5805                                 }
5806                         } else {
5807                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5808                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5809
5810                                 if (!extend && !add && _region->selection_size() > 1) {
5811                                         _editor->get_selection().clear_points();
5812                                         _region->unique_select (_primary);
5813                                         changed = true;
5814                                 } else if (extend) {
5815                                         _region->note_selected (_primary, true, true);
5816                                         changed = true;
5817                                 } else {
5818                                         /* it was added during button press */
5819                                         changed = true;
5820
5821                                 }
5822                         }
5823
5824                         if (changed) {
5825                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5826                                 _editor->commit_reversible_selection_op();
5827                         }
5828                 }
5829         } else {
5830                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5831         }
5832 }
5833
5834 void
5835 NoteDrag::aborted (bool)
5836 {
5837         /* XXX: TODO */
5838 }
5839
5840 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5841 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5842         : Drag (editor, atv->base_item ())
5843         , _ranges (r)
5844         , _y_origin (atv->y_position())
5845         , _nothing_to_drag (false)
5846 {
5847         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5848         setup (atv->lines ());
5849 }
5850
5851 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5852 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5853         : Drag (editor, rv->get_canvas_group ())
5854         , _ranges (r)
5855         , _y_origin (rv->get_time_axis_view().y_position())
5856         , _nothing_to_drag (false)
5857         , _integral (false)
5858 {
5859         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5860
5861         list<boost::shared_ptr<AutomationLine> > lines;
5862
5863         AudioRegionView*      audio_view;
5864         AutomationRegionView* automation_view;
5865         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5866                 lines.push_back (audio_view->get_gain_line ());
5867         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5868                 lines.push_back (automation_view->line ());
5869                 _integral = true;
5870         } else {
5871                 error << _("Automation range drag created for invalid region type") << endmsg;
5872         }
5873
5874         setup (lines);
5875 }
5876
5877 /** @param lines AutomationLines to drag.
5878  *  @param offset Offset from the session start to the points in the AutomationLines.
5879  */
5880 void
5881 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5882 {
5883         /* find the lines that overlap the ranges being dragged */
5884         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5885         while (i != lines.end ()) {
5886                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5887                 ++j;
5888
5889                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5890
5891                 /* check this range against all the AudioRanges that we are using */
5892                 list<AudioRange>::const_iterator k = _ranges.begin ();
5893                 while (k != _ranges.end()) {
5894                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5895                                 break;
5896                         }
5897                         ++k;
5898                 }
5899
5900                 /* add it to our list if it overlaps at all */
5901                 if (k != _ranges.end()) {
5902                         Line n;
5903                         n.line = *i;
5904                         n.state = 0;
5905                         n.range = r;
5906                         _lines.push_back (n);
5907                 }
5908
5909                 i = j;
5910         }
5911
5912         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5913 }
5914
5915 double
5916 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5917 {
5918         return 1.0 - ((global_y - _y_origin) / line->height());
5919 }
5920
5921 double
5922 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5923 {
5924         const double v = list->eval(x);
5925         return _integral ? rint(v) : v;
5926 }
5927
5928 void
5929 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5930 {
5931         Drag::start_grab (event, cursor);
5932
5933         /* Get line states before we start changing things */
5934         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5935                 i->state = &i->line->get_state ();
5936                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5937         }
5938
5939         if (_ranges.empty()) {
5940
5941                 /* No selected time ranges: drag all points */
5942                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5943                         uint32_t const N = i->line->npoints ();
5944                         for (uint32_t j = 0; j < N; ++j) {
5945                                 i->points.push_back (i->line->nth (j));
5946                         }
5947                 }
5948
5949         }
5950
5951         if (_nothing_to_drag) {
5952                 return;
5953         }
5954 }
5955
5956 void
5957 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5958 {
5959         if (_nothing_to_drag && !first_move) {
5960                 return;
5961         }
5962
5963         if (first_move) {
5964                 _editor->begin_reversible_command (_("automation range move"));
5965
5966                 if (!_ranges.empty()) {
5967
5968                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5969
5970                                 framecnt_t const half = (i->start + i->end) / 2;
5971
5972                                 /* find the line that this audio range starts in */
5973                                 list<Line>::iterator j = _lines.begin();
5974                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5975                                         ++j;
5976                                 }
5977
5978                                 if (j != _lines.end()) {
5979                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5980
5981                                 /* j is the line that this audio range starts in; fade into it;
5982                                    64 samples length plucked out of thin air.
5983                                 */
5984
5985                                         framepos_t a = i->start + 64;
5986                                         if (a > half) {
5987                                                 a = half;
5988                                         }
5989
5990                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5991                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5992
5993                                         XMLNode &before = the_list->get_state();
5994                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5995                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5996
5997                                         if (add_p || add_q) {
5998                                                 _editor->session()->add_command (
5999                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6000                                         }
6001                                 }
6002
6003                                 /* same thing for the end */
6004
6005                                 j = _lines.begin();
6006                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6007                                         ++j;
6008                                 }
6009
6010                                 if (j != _lines.end()) {
6011                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6012
6013                                         /* j is the line that this audio range starts in; fade out of it;
6014                                            64 samples length plucked out of thin air.
6015                                         */
6016
6017                                         framepos_t b = i->end - 64;
6018                                         if (b < half) {
6019                                                 b = half;
6020                                         }
6021
6022                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6023                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6024
6025                                         XMLNode &before = the_list->get_state();
6026                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6027                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6028
6029                                         if (add_p || add_q) {
6030                                                 _editor->session()->add_command (
6031                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6032                                         }
6033                                 }
6034                         }
6035
6036                         _nothing_to_drag = true;
6037
6038                         /* Find all the points that should be dragged and put them in the relevant
6039                            points lists in the Line structs.
6040                         */
6041
6042                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6043
6044                                 uint32_t const N = i->line->npoints ();
6045                                 for (uint32_t j = 0; j < N; ++j) {
6046
6047                                         /* here's a control point on this line */
6048                                         ControlPoint* p = i->line->nth (j);
6049                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6050
6051                                         /* see if it's inside a range */
6052                                         list<AudioRange>::const_iterator k = _ranges.begin ();
6053                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6054                                                 ++k;
6055                                         }
6056
6057                                         if (k != _ranges.end()) {
6058                                                 /* dragging this point */
6059                                                 _nothing_to_drag = false;
6060                                                 i->points.push_back (p);
6061                                         }
6062                                 }
6063                         }
6064                 }
6065
6066                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6067                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6068                 }
6069         }
6070
6071         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6072                 float const f = y_fraction (l->line, current_pointer_y());
6073                 /* we are ignoring x position for this drag, so we can just pass in anything */
6074                 pair<double, float> result;
6075                 uint32_t ignored;
6076                 result = l->line->drag_motion (0, f, true, false, ignored);
6077                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
6078         }
6079 }
6080
6081 void
6082 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6083 {
6084         if (_nothing_to_drag || !motion_occurred) {
6085                 return;
6086         }
6087
6088         motion (event, false);
6089         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6090                 i->line->end_drag (false, 0);
6091         }
6092
6093         _editor->commit_reversible_command ();
6094 }
6095
6096 void
6097 AutomationRangeDrag::aborted (bool)
6098 {
6099         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6100                 i->line->reset ();
6101         }
6102 }
6103
6104 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6105         : view (v)
6106         , initial_time_axis_view (itav)
6107 {
6108         /* note that time_axis_view may be null if the regionview was created
6109          * as part of a copy operation.
6110          */
6111         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6112         layer = v->region()->layer ();
6113         initial_y = v->get_canvas_group()->position().y;
6114         initial_playlist = v->region()->playlist ();
6115         initial_position = v->region()->position ();
6116         initial_end = v->region()->position () + v->region()->length ();
6117 }
6118
6119 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6120         : Drag (e, i->canvas_item ())
6121         , _region_view (r)
6122         , _patch_change (i)
6123         , _cumulative_dx (0)
6124 {
6125         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6126                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6127                                                    grab_frame()));
6128 }
6129
6130 void
6131 PatchChangeDrag::motion (GdkEvent* ev, bool)
6132 {
6133         framepos_t f = adjusted_current_frame (ev);
6134         boost::shared_ptr<Region> r = _region_view->region ();
6135         f = max (f, r->position ());
6136         f = min (f, r->last_frame ());
6137
6138         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6139         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6140         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6141         _cumulative_dx = dxu;
6142 }
6143
6144 void
6145 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6146 {
6147         if (!movement_occurred) {
6148                 if (was_double_click()) {
6149                         _region_view->edit_patch_change (_patch_change);
6150                 }
6151                 return;
6152         }
6153
6154         boost::shared_ptr<Region> r (_region_view->region ());
6155         framepos_t f = adjusted_current_frame (ev);
6156         f = max (f, r->position ());
6157         f = min (f, r->last_frame ());
6158
6159         _region_view->move_patch_change (
6160                 *_patch_change,
6161                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6162                 );
6163 }
6164
6165 void
6166 PatchChangeDrag::aborted (bool)
6167 {
6168         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6169 }
6170
6171 void
6172 PatchChangeDrag::setup_pointer_frame_offset ()
6173 {
6174         boost::shared_ptr<Region> region = _region_view->region ();
6175         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6176 }
6177
6178 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6179         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6180         , _region_view (rv)
6181 {
6182
6183 }
6184
6185 void
6186 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6187 {
6188         _region_view->update_drag_selection (
6189                 x1, x2, y1, y2,
6190                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6191 }
6192
6193 void
6194 MidiRubberbandSelectDrag::deselect_things ()
6195 {
6196         /* XXX */
6197 }
6198
6199 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6200         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6201         , _region_view (rv)
6202 {
6203         _vertical_only = true;
6204 }
6205
6206 void
6207 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6208 {
6209         double const y = _region_view->midi_view()->y_position ();
6210
6211         y1 = max (0.0, y1 - y);
6212         y2 = max (0.0, y2 - y);
6213
6214         _region_view->update_vertical_drag_selection (
6215                 y1,
6216                 y2,
6217                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6218                 );
6219 }
6220
6221 void
6222 MidiVerticalSelectDrag::deselect_things ()
6223 {
6224         /* XXX */
6225 }
6226
6227 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6228         : RubberbandSelectDrag (e, i)
6229 {
6230
6231 }
6232
6233 void
6234 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6235 {
6236         if (drag_in_progress) {
6237                 /* We just want to select things at the end of the drag, not during it */
6238                 return;
6239         }
6240
6241         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6242
6243         _editor->begin_reversible_selection_op (X_("rubberband selection"));
6244
6245         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6246
6247         _editor->commit_reversible_selection_op ();
6248 }
6249
6250 void
6251 EditorRubberbandSelectDrag::deselect_things ()
6252 {
6253         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6254
6255         _editor->selection->clear_tracks();
6256         _editor->selection->clear_regions();
6257         _editor->selection->clear_points ();
6258         _editor->selection->clear_lines ();
6259         _editor->selection->clear_midi_notes ();
6260
6261         _editor->commit_reversible_selection_op();
6262 }
6263
6264 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6265         : Drag (e, i)
6266         , _region_view (rv)
6267         , _drag_rect (0)
6268 {
6269         _note[0] = _note[1] = 0;
6270 }
6271
6272 NoteCreateDrag::~NoteCreateDrag ()
6273 {
6274         delete _drag_rect;
6275 }
6276
6277 framecnt_t
6278 NoteCreateDrag::grid_frames (framepos_t t) const
6279 {
6280
6281         const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6282         const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6283
6284         return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6285                 - _region_view->region_beats_to_region_frames (t_beats);
6286 }
6287
6288 void
6289 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6290 {
6291         Drag::start_grab (event, cursor);
6292
6293         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6294         TempoMap& map (_editor->session()->tempo_map());
6295
6296         const framepos_t pf = _drags->current_pointer_frame ();
6297         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6298
6299         const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6300
6301         double eqaf = map.exact_qn_at_frame (pf, divisions);
6302
6303         if (divisions != 0) {
6304
6305                 const double qaf = map.quarter_note_at_frame (pf);
6306
6307                 /* Hack so that we always snap to the note that we are over, instead of snapping
6308                    to the next one if we're more than halfway through the one we're over.
6309                 */
6310
6311                 const double rem = eqaf - qaf;
6312                 if (rem >= 0.0) {
6313                         eqaf -= grid_beats.to_double();
6314                 }
6315         }
6316
6317         _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6318         /* minimum initial length is grid beats */
6319         _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6320
6321         double const x0 = _editor->sample_to_pixel (_note[0]);
6322         double const x1 = _editor->sample_to_pixel (_note[1]);
6323         double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6324
6325         _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6326         _drag_rect->set_outline_all ();
6327         _drag_rect->set_outline_color (0xffffff99);
6328         _drag_rect->set_fill_color (0xffffff66);
6329 }
6330
6331 void
6332 NoteCreateDrag::motion (GdkEvent* event, bool)
6333 {
6334         TempoMap& map (_editor->session()->tempo_map());
6335         const framepos_t pf = _drags->current_pointer_frame ();
6336         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6337         double eqaf = map.exact_qn_at_frame (pf, divisions);
6338
6339         if (divisions != 0) {
6340
6341                 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6342
6343                 const double qaf = map.quarter_note_at_frame (pf);
6344                 /* Hack so that we always snap to the note that we are over, instead of snapping
6345                    to the next one if we're more than halfway through the one we're over.
6346                 */
6347
6348                 const double rem = eqaf - qaf;
6349                 if (rem >= 0.0) {
6350                         eqaf -= grid_beats.to_double();
6351                 }
6352
6353                 eqaf += grid_beats.to_double();
6354         }
6355         _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6356
6357         double const x0 = _editor->sample_to_pixel (_note[0]);
6358         double const x1 = _editor->sample_to_pixel (_note[1]);
6359         _drag_rect->set_x0 (std::min(x0, x1));
6360         _drag_rect->set_x1 (std::max(x0, x1));
6361 }
6362
6363 void
6364 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6365 {
6366         /* we create a note even if there was no movement */
6367         framepos_t const start = min (_note[0], _note[1]);
6368         framepos_t const start_sess_rel = start + _region_view->region()->position();
6369         framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6370         framecnt_t const g = grid_frames (start);
6371
6372         if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6373                 length = g;
6374         }
6375
6376         TempoMap& map (_editor->session()->tempo_map());
6377         const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6378         Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6379
6380         _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6381 }
6382
6383 double
6384 NoteCreateDrag::y_to_region (double y) const
6385 {
6386         double x = 0;
6387         _region_view->get_canvas_group()->canvas_to_item (x, y);
6388         return y;
6389 }
6390
6391 void
6392 NoteCreateDrag::aborted (bool)
6393 {
6394
6395 }
6396
6397 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6398         : Drag (e, i)
6399         , _region_view (rv)
6400         , _last_pos (0)
6401         , _last_y (0.0)
6402 {
6403 }
6404
6405 HitCreateDrag::~HitCreateDrag ()
6406 {
6407 }
6408
6409 void
6410 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6411 {
6412         Drag::start_grab (event, cursor);
6413
6414         TempoMap& map (_editor->session()->tempo_map());
6415
6416         const framepos_t pf = _drags->current_pointer_frame ();
6417         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6418
6419         const double eqaf = map.exact_qn_at_frame (pf, divisions);
6420
6421         boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6422
6423         if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6424                 return;
6425         }
6426
6427         const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6428         const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6429
6430         Evoral::Beats length = _region_view->get_grid_beats (pf);
6431
6432         _region_view->create_note_at (start, y, length, event->button.state, false);
6433
6434         _last_pos = start;
6435         _last_y = y;
6436 }
6437
6438 void
6439 HitCreateDrag::motion (GdkEvent* event, bool)
6440 {
6441         TempoMap& map (_editor->session()->tempo_map());
6442
6443         const framepos_t pf = _drags->current_pointer_frame ();
6444         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6445
6446         if (divisions == 0) {
6447                 return;
6448         }
6449
6450         const double eqaf = map.exact_qn_at_frame (pf, divisions);
6451         const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6452         const double y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6453
6454         if (_last_pos == start && y == _last_y) {
6455                 return;
6456         }
6457
6458         Evoral::Beats length = _region_view->get_grid_beats (pf);
6459
6460         boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6461         if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6462                 return;
6463         }
6464
6465         _region_view->create_note_at (start, y, length, event->button.state, false);
6466
6467         _last_pos = start;
6468         _last_y = y;
6469 }
6470
6471 void
6472 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6473 {
6474
6475 }
6476
6477 double
6478 HitCreateDrag::y_to_region (double y) const
6479 {
6480         double x = 0;
6481         _region_view->get_canvas_group()->canvas_to_item (x, y);
6482         return y;
6483 }
6484
6485 void
6486 HitCreateDrag::aborted (bool)
6487 {
6488         // umm..
6489 }
6490
6491 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6492         : Drag (e, i)
6493         , arv (rv)
6494         , start (start_yn)
6495 {
6496         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
6497 }
6498
6499 void
6500 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6501 {
6502         Drag::start_grab (event, cursor);
6503 }
6504
6505 void
6506 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6507 {
6508         double distance;
6509         double new_length;
6510         framecnt_t len;
6511
6512         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6513
6514         if (start) {
6515                 distance = _drags->current_pointer_x() - grab_x();
6516                 len = ar->fade_in()->back()->when;
6517         } else {
6518                 distance = grab_x() - _drags->current_pointer_x();
6519                 len = ar->fade_out()->back()->when;
6520         }
6521
6522         /* how long should it be ? */
6523
6524         new_length = len + _editor->pixel_to_sample (distance);
6525
6526         /* now check with the region that this is legal */
6527
6528         new_length = ar->verify_xfade_bounds (new_length, start);
6529
6530         if (start) {
6531                 arv->reset_fade_in_shape_width (ar, new_length);
6532         } else {
6533                 arv->reset_fade_out_shape_width (ar, new_length);
6534         }
6535 }
6536
6537 void
6538 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6539 {
6540         double distance;
6541         double new_length;
6542         framecnt_t len;
6543
6544         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6545
6546         if (start) {
6547                 distance = _drags->current_pointer_x() - grab_x();
6548                 len = ar->fade_in()->back()->when;
6549         } else {
6550                 distance = grab_x() - _drags->current_pointer_x();
6551                 len = ar->fade_out()->back()->when;
6552         }
6553
6554         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6555
6556         _editor->begin_reversible_command ("xfade trim");
6557         ar->playlist()->clear_owned_changes ();
6558
6559         if (start) {
6560                 ar->set_fade_in_length (new_length);
6561         } else {
6562                 ar->set_fade_out_length (new_length);
6563         }
6564
6565         /* Adjusting the xfade may affect other regions in the playlist, so we need
6566            to get undo Commands from the whole playlist rather than just the
6567            region.
6568         */
6569
6570         vector<Command*> cmds;
6571         ar->playlist()->rdiff (cmds);
6572         _editor->session()->add_commands (cmds);
6573         _editor->commit_reversible_command ();
6574
6575 }
6576
6577 void
6578 CrossfadeEdgeDrag::aborted (bool)
6579 {
6580         if (start) {
6581                 // arv->redraw_start_xfade ();
6582         } else {
6583                 // arv->redraw_end_xfade ();
6584         }
6585 }
6586
6587 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6588         : Drag (e, item, true)
6589         , line (new EditorCursor (*e))
6590 {
6591         line->set_position (pos);
6592         line->show ();
6593         line->track_canvas_item().reparent (_editor->_drag_motion_group);
6594 }
6595
6596 RegionCutDrag::~RegionCutDrag ()
6597 {
6598         delete line;
6599 }
6600
6601 void
6602 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6603 {
6604         Drag::start_grab (event, c);
6605         motion (event, false);
6606 }
6607
6608 void
6609 RegionCutDrag::motion (GdkEvent* event, bool)
6610 {
6611         framepos_t pos = _drags->current_pointer_frame();
6612         _editor->snap_to_with_modifier (pos, event);
6613
6614         line->set_position (pos);
6615 }
6616
6617 void
6618 RegionCutDrag::finished (GdkEvent* event, bool)
6619 {
6620         _editor->get_track_canvas()->canvas()->re_enter();
6621
6622         framepos_t pos = _drags->current_pointer_frame();
6623         _editor->snap_to_with_modifier (pos, event);
6624
6625         line->hide ();
6626
6627         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6628
6629         if (rs.empty()) {
6630                 return;
6631         }
6632
6633         _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state),
6634                                    false);
6635 }
6636
6637 void
6638 RegionCutDrag::aborted (bool)
6639 {
6640 }
6641
6642 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6643         : Drag (e, item, true)
6644 {
6645 }
6646
6647 void
6648 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6649 {
6650         Drag::start_grab (event, c);
6651
6652         framepos_t where = _editor->canvas_event_sample(event);
6653
6654         _editor->_dragging_playhead = true;
6655
6656         _editor->playhead_cursor->set_position (where);
6657 }
6658
6659 void
6660 RulerZoomDrag::motion (GdkEvent* event, bool)
6661 {
6662         framepos_t where = _editor->canvas_event_sample(event);
6663
6664         _editor->playhead_cursor->set_position (where);
6665
6666         const double movement_limit = 20.0;
6667         const double scale = 1.08;
6668         const double y_delta = last_pointer_y() - current_pointer_y();
6669
6670         if (y_delta > 0 && y_delta < movement_limit) {
6671                 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6672         } else if (y_delta < 0 && y_delta > -movement_limit) {
6673                 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
6674         }
6675 }
6676
6677 void
6678 RulerZoomDrag::finished (GdkEvent*, bool)
6679 {
6680         _editor->_dragging_playhead = false;
6681
6682         Session* s = _editor->session ();
6683         if (s) {
6684                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
6685                 _editor->_pending_locate_request = true;
6686         }
6687
6688 }
6689
6690 void
6691 RulerZoomDrag::aborted (bool)
6692 {
6693 }