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