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