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