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