Log-scale/relative automation point dragging
[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_frame (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_frame = _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_frame = _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_frame_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_frame (0)
239         , _grab_frame (0)
240         , _last_pointer_frame (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_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
271
272         setup_pointer_frame_offset ();
273         _grab_frame = adjusted_frame (_raw_grab_frame, event).frame;
274         _last_pointer_frame = _grab_frame;
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 MusicFrame
330 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
331 {
332         MusicFrame pos (0, 0);
333
334         if (f > _pointer_frame_offset) {
335                 pos.frame = f - _pointer_frame_offset;
336         }
337
338         if (snap) {
339                 _editor->snap_to_with_modifier (pos, event);
340         }
341
342         return pos;
343 }
344
345 framepos_t
346 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
347 {
348         return adjusted_frame (_drags->current_pointer_frame (), event, snap).frame;
349 }
350
351 frameoffset_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 (MusicFrame pos)
388 {
389         TempoMap& map (_editor->session()->tempo_map());
390         MusicFrame snap (pos);
391         _editor->snap_to (snap, ARDOUR::RoundNearest, false, true);
392         _snap_delta = snap.frame - pos.frame;
393
394         _snap_delta_music = 0.0;
395
396         if (_snap_delta != 0) {
397                 _snap_delta_music = map.exact_qn_at_frame (snap.frame, snap.division) - map.exact_qn_at_frame (pos.frame, 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<framecnt_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_frame () - _raw_grab_frame) >= 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_frame = adjusted_current_frame (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 (framepos_t frame)
514 {
515         _editor->verbose_cursor()->set_time (frame);
516         _editor->verbose_cursor()->show ();
517 }
518
519 void
520 Drag::show_verbose_cursor_duration (framepos_t start, framepos_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                 framecnt_t pos = grab_frame();
539                 /* not that the frame rate used here can be affected by pull up/down which
540                    might be wrong.
541                 */
542                 framecnt_t len = map.frame_at_beat (max (0.0, map.beat_at_frame (pos)) + 1.0) - pos;
543                 return view->add_region (grab_frame(), 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.frame);
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, MusicFrame* pending_region_position)
664 {
665         /* compute the amount of pointer motion in frames, 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_frame (_drags->current_pointer_frame (), event, false);
674
675         framecnt_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->frame >= sync_offset)) {
683
684                 framecnt_t const sd = snap_delta (event->button.state);
685                 MusicFrame sync_snap (pending_region_position->frame + (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.frame) - sd, 0);
691                 }
692         } else {
693                 *pending_region_position = _last_position;
694         }
695
696         if (pending_region_position->frame > max_framepos - _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->frame != _last_position.frame) && x_move_allowed) {
705
706                 /* x movement since last time (in pixels) */
707                 dx = _editor->sample_to_pixel_unrounded (pending_region_position->frame - _last_position.frame);
708
709                 /* total x movement */
710                 framecnt_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                         frameoffset_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 = MusicFrame (pending_region_position->frame - 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         MusicFrame 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_frame (_last_position.frame, _last_position.division);
961         double const qn_delta = tmap.exact_qn_at_frame (pending_region_position.frame, 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.frame);
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_frame (rv->get_position());
1208                         framepos_t const x_pos_music = tmap.frame_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.frame);
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.frame != _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*/, MusicFrame last_position, int32_t const ev_state)
1472 {
1473         RegionSelection new_views;
1474         PlaylistSet modified_playlists;
1475         RouteTimeAxisView* new_time_axis_view = 0;
1476         framecnt_t const drag_delta = _primary->region()->position() - _last_position.frame;
1477
1478         TempoMap& tmap (_editor->session()->tempo_map());
1479         const double last_pos_qn = tmap.exact_qn_at_frame (last_position.frame, 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                 MusicFrame 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         MusicFrame 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         framecnt_t const drag_delta = _primary->region()->position() - last_position.frame;
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_frame (last_position.frame, 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                 MusicFrame 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.frame, last_position.division);
1747                         } else {
1748                                 if (rv->region()->position_lock_style() == AudioTime) {
1749                                         /* move by frame offset */
1750                                         rv->region()->set_position (where.frame, 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         MusicFrame                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.frame, 1.0, false, where.division, quarter_note, true);
1879         } else {
1880                 dest_playlist->add_region (region, where.frame, 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         double speed = 1;
1971         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1972         if (rtv && rtv->is_track()) {
1973                 speed = rtv->track()->speed ();
1974         }
1975
1976         _last_position = MusicFrame (static_cast<framepos_t> (_primary->region()->position() / speed), 0);
1977 }
1978
1979 void
1980 RegionMoveDrag::setup_pointer_frame_offset ()
1981 {
1982         _pointer_frame_offset = raw_grab_frame() - _last_position.frame;
1983 }
1984
1985 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1986         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1987 {
1988         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1989
1990         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1991                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1992
1993         _primary = v->view()->create_region_view (r, false, false);
1994
1995         _primary->get_canvas_group()->show ();
1996         _primary->set_position (pos, 0);
1997         _views.push_back (DraggingView (_primary, this, v));
1998
1999         _last_position = MusicFrame (pos, 0);
2000
2001         _item = _primary->get_canvas_group ();
2002 }
2003
2004 void
2005 RegionInsertDrag::finished (GdkEvent * event, bool)
2006 {
2007         int pos = _views.front().time_axis_view;
2008         assert(pos >= 0 && pos < (int)_time_axis_views.size());
2009
2010         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
2011
2012         _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
2013         _primary->get_canvas_group()->set_y_position (0);
2014
2015         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
2016
2017         _editor->begin_reversible_command (Operations::insert_region);
2018         playlist->clear_changes ();
2019         _editor->snap_to_with_modifier (_last_position, event);
2020
2021         playlist->add_region (_primary->region (), _last_position.frame, 1.0, false, _last_position.division);
2022
2023         // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
2024         if (Config->get_edit_mode() == Ripple) {
2025                 playlist->ripple (_last_position.frame, _primary->region()->length(), _primary->region());
2026         }
2027
2028         _editor->session()->add_command (new StatefulDiffCommand (playlist));
2029         _editor->commit_reversible_command ();
2030
2031         delete _primary;
2032         _primary = 0;
2033         _views.clear ();
2034 }
2035
2036 void
2037 RegionInsertDrag::aborted (bool)
2038 {
2039         delete _primary;
2040         _primary = 0;
2041         _views.clear ();
2042 }
2043
2044 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2045         : RegionMoveDrag (e, i, p, v, false, false)
2046 {
2047         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
2048 }
2049
2050 struct RegionSelectionByPosition {
2051         bool operator() (RegionView*a, RegionView* b) {
2052                 return a->region()->position () < b->region()->position();
2053         }
2054 };
2055
2056 void
2057 RegionSpliceDrag::motion (GdkEvent* event, bool)
2058 {
2059         /* Which trackview is this ? */
2060
2061         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2062         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2063
2064         /* The region motion is only processed if the pointer is over
2065            an audio track.
2066         */
2067
2068         if (!tv || !tv->is_track()) {
2069                 /* To make sure we hide the verbose canvas cursor when the mouse is
2070                    not held over an audio track.
2071                 */
2072                 _editor->verbose_cursor()->hide ();
2073                 return;
2074         } else {
2075                 _editor->verbose_cursor()->show ();
2076         }
2077
2078         int dir;
2079
2080         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
2081                 dir = 1;
2082         } else {
2083                 dir = -1;
2084         }
2085
2086         RegionSelection copy;
2087         _editor->selection->regions.by_position(copy);
2088
2089         framepos_t const pf = adjusted_current_frame (event);
2090
2091         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2092
2093                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
2094
2095                 if (!atv) {
2096                         continue;
2097                 }
2098
2099                 boost::shared_ptr<Playlist> playlist;
2100
2101                 if ((playlist = atv->playlist()) == 0) {
2102                         continue;
2103                 }
2104
2105                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
2106                         continue;
2107                 }
2108
2109                 if (dir > 0) {
2110                         if (pf < (*i)->region()->last_frame() + 1) {
2111                                 continue;
2112                         }
2113                 } else {
2114                         if (pf > (*i)->region()->first_frame()) {
2115                                 continue;
2116                         }
2117                 }
2118
2119
2120                 playlist->shuffle ((*i)->region(), dir);
2121         }
2122 }
2123
2124 void
2125 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2126 {
2127         RegionMoveDrag::finished (event, movement_occurred);
2128 }
2129
2130 void
2131 RegionSpliceDrag::aborted (bool)
2132 {
2133         /* XXX: TODO */
2134 }
2135
2136 /***
2137  * ripple mode...
2138  */
2139
2140 void
2141 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2142 {
2143
2144         boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2145
2146         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2147         RegionSelection to_ripple;
2148         for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2149                 if ((*i)->position() >= where) {
2150                         to_ripple.push_back (rtv->view()->find_view(*i));
2151                 }
2152         }
2153
2154         for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2155                 if (!exclude.contains (*i)) {
2156                         // the selection has already been added to _views
2157
2158                         if (drag_in_progress) {
2159                                 // do the same things that RegionMotionDrag::motion does when
2160                                 // first_move is true, for the region views that we're adding
2161                                 // to _views this time
2162
2163                                 (*i)->drag_start();
2164                                 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2165                                 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2166                                 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2167                                 rvg->reparent (_editor->_drag_motion_group);
2168
2169                                 // we only need to move in the y direction
2170                                 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2171                                 fudge.x = 0;
2172                                 rvg->move (fudge);
2173
2174                         }
2175                         _views.push_back (DraggingView (*i, this, tav));
2176                 }
2177         }
2178 }
2179
2180 void
2181 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2182 {
2183
2184         for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2185                 // we added all the regions after the selection
2186
2187                 std::list<DraggingView>::iterator to_erase = i++;
2188                 if (!_editor->selection->regions.contains (to_erase->view)) {
2189                         // restore the non-selected regions to their original playlist & positions,
2190                         // and then ripple them back by the length of the regions that were dragged away
2191                         // do the same things as RegionMotionDrag::aborted
2192
2193                         RegionView *rv = to_erase->view;
2194                         TimeAxisView* tv = &(rv->get_time_axis_view ());
2195                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2196                         assert (rtv);
2197
2198                         // plonk them back onto their own track
2199                         rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2200                         rv->get_canvas_group()->set_y_position (0);
2201                         rv->drag_end ();
2202
2203                         if (move_regions) {
2204                                 // move the underlying region to match the view
2205                                 rv->region()->set_position (rv->region()->position() + amount);
2206                         } else {
2207                                 // restore the view to match the underlying region's original position
2208                                 rv->move(-amount, 0);   // second parameter is y delta - seems 0 is OK
2209                         }
2210
2211                         rv->set_height (rtv->view()->child_height ());
2212                         _views.erase (to_erase);
2213                 }
2214         }
2215 }
2216
2217 bool
2218 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2219 {
2220         if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2221                 if (delta_track) {
2222                         return allow_moves_across_tracks;
2223                 } else {
2224                         return true;
2225                 }
2226         }
2227         return false;
2228 }
2229
2230 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2231         : RegionMoveDrag (e, i, p, v, false, false)
2232 {
2233         DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2234         // compute length of selection
2235         RegionSelection selected_regions = _editor->selection->regions;
2236         selection_length = selected_regions.end_frame() - selected_regions.start();
2237
2238         // we'll only allow dragging to another track in ripple mode if all the regions
2239         // being dragged start off on the same track
2240         allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2241         prev_tav = NULL;
2242         prev_amount = 0;
2243         exclude = new RegionList;
2244         for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2245                 exclude->push_back((*i)->region());
2246         }
2247
2248         // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2249         RegionSelection copy;
2250         selected_regions.by_position(copy); // get selected regions sorted by position into copy
2251
2252         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2253         std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2254
2255         for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2256                 // find ripple start point on each applicable playlist
2257                 RegionView *first_selected_on_this_track = NULL;
2258                 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2259                         if ((*i)->region()->playlist() == (*pi)) {
2260                                 // region is on this playlist - it's the first, because they're sorted
2261                                 first_selected_on_this_track = *i;
2262                                 break;
2263                         }
2264                 }
2265                 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2266                 add_all_after_to_views (
2267                                 &first_selected_on_this_track->get_time_axis_view(),
2268                                 first_selected_on_this_track->region()->position(),
2269                                 selected_regions, false);
2270         }
2271
2272         if (allow_moves_across_tracks) {
2273                 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2274         } else {
2275                 orig_tav = NULL;
2276         }
2277
2278 }
2279
2280 void
2281 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2282 {
2283         /* Which trackview is this ? */
2284
2285         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2286         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2287
2288         /* The region motion is only processed if the pointer is over
2289            an audio track.
2290          */
2291
2292         if (!tv || !tv->is_track()) {
2293                 /* To make sure we hide the verbose canvas cursor when the mouse is
2294                    not held over an audiotrack.
2295                  */
2296                 _editor->verbose_cursor()->hide ();
2297                 return;
2298         }
2299
2300         framepos_t where = adjusted_current_frame (event);
2301         assert (where >= 0);
2302         MusicFrame after (0, 0);
2303         double delta = compute_x_delta (event, &after);
2304
2305         framecnt_t amount = _editor->pixel_to_sample (delta);
2306
2307         if (allow_moves_across_tracks) {
2308                 // all the originally selected regions were on the same track
2309
2310                 framecnt_t adjust = 0;
2311                 if (prev_tav && tv != prev_tav) {
2312                         // dragged onto a different track
2313                         // remove the unselected regions from _views, restore them to their original positions
2314                         // and add the regions after the drop point on the new playlist to _views instead.
2315                         // undo the effect of rippling the previous playlist, and include the effect of removing
2316                         // the dragged region(s) from this track
2317
2318                         remove_unselected_from_views (prev_amount, false);
2319                         // ripple previous playlist according to the regions that have been removed onto the new playlist
2320                         prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2321                         prev_amount = 0;
2322
2323                         // move just the selected regions
2324                         RegionMoveDrag::motion(event, first_move);
2325
2326                         // ensure that the ripple operation on the new playlist inserts selection_length time
2327                         adjust = selection_length;
2328                         // ripple the new current playlist
2329                         tv->playlist()->ripple (where, amount+adjust, exclude);
2330
2331                         // add regions after point where drag entered this track to subsequent ripples
2332                         add_all_after_to_views (tv, where, _editor->selection->regions, true);
2333
2334                 } else {
2335                         // motion on same track
2336                         RegionMoveDrag::motion(event, first_move);
2337                 }
2338                 prev_tav = tv;
2339
2340                 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2341                 prev_position = where;
2342         } else {
2343                 // selection encompasses multiple tracks - just drag
2344                 // cross-track drags are forbidden
2345                 RegionMoveDrag::motion(event, first_move);
2346         }
2347
2348         if (!_x_constrained) {
2349                 prev_amount += amount;
2350         }
2351
2352         _last_position = after;
2353 }
2354
2355 void
2356 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2357 {
2358         if (!movement_occurred) {
2359
2360                 /* just a click */
2361
2362                 if (was_double_click() && !_views.empty()) {
2363                         DraggingView dv = _views.front();
2364                         _editor->edit_region (dv.view);
2365                 }
2366
2367                 return;
2368         }
2369
2370         _editor->begin_reversible_command(_("Ripple drag"));
2371
2372         // remove the regions being rippled from the dragging view, updating them to
2373         // their new positions
2374         remove_unselected_from_views (prev_amount, true);
2375
2376         if (allow_moves_across_tracks) {
2377                 if (orig_tav) {
2378                         // if regions were dragged across tracks, we've rippled any later
2379                         // regions on the track the regions were dragged off, so we need
2380                         // to add the original track to the undo record
2381                         orig_tav->playlist()->clear_changes();
2382                         vector<Command*> cmds;
2383                         orig_tav->playlist()->rdiff (cmds);
2384                         _editor->session()->add_commands (cmds);
2385                 }
2386                 if (prev_tav && prev_tav != orig_tav) {
2387                         prev_tav->playlist()->clear_changes();
2388                         vector<Command*> cmds;
2389                         prev_tav->playlist()->rdiff (cmds);
2390                         _editor->session()->add_commands (cmds);
2391                 }
2392         } else {
2393                 // selection spanned multiple tracks - all will need adding to undo record
2394
2395                 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2396                 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2397
2398                 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2399                         (*pi)->clear_changes();
2400                         vector<Command*> cmds;
2401                         (*pi)->rdiff (cmds);
2402                         _editor->session()->add_commands (cmds);
2403                 }
2404         }
2405
2406         // other modified playlists are added to undo by RegionMoveDrag::finished()
2407         RegionMoveDrag::finished (event, movement_occurred);
2408         _editor->commit_reversible_command();
2409 }
2410
2411 void
2412 RegionRippleDrag::aborted (bool movement_occurred)
2413 {
2414         RegionMoveDrag::aborted (movement_occurred);
2415         _views.clear ();
2416 }
2417
2418
2419 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2420         : Drag (e, i),
2421           _view (dynamic_cast<MidiTimeAxisView*> (v))
2422 {
2423         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2424
2425         assert (_view);
2426 }
2427
2428 void
2429 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2430 {
2431
2432         if (first_move) {
2433                 _editor->begin_reversible_command (_("create region"));
2434                 _region = add_midi_region (_view, false);
2435                 _view->playlist()->freeze ();
2436         } else {
2437
2438                 if (_region) {
2439                         framepos_t const f = adjusted_current_frame (event);
2440                         if (f <= grab_frame()) {
2441                                 _region->set_initial_position (f);
2442                         }
2443
2444                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2445                            so that if this region is duplicated, its duplicate starts on
2446                            a snap point rather than 1 frame after a snap point.  Otherwise things get
2447                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
2448                            place snapped notes at the start of the region.
2449                         */
2450                         if (f != grab_frame()) {
2451                                 framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2452                                 _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2453                         }
2454                 }
2455         }
2456 }
2457
2458 void
2459 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2460 {
2461         if (!movement_occurred) {
2462                 add_midi_region (_view, true);
2463         } else {
2464                 _view->playlist()->thaw ();
2465                 _editor->commit_reversible_command();
2466         }
2467 }
2468
2469 void
2470 RegionCreateDrag::aborted (bool)
2471 {
2472         if (_region) {
2473                 _view->playlist()->thaw ();
2474         }
2475
2476         /* XXX */
2477 }
2478
2479 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2480         : Drag (e, i)
2481         , region (0)
2482         , relative (false)
2483         , at_front (true)
2484         , _was_selected (false)
2485         , _snap_delta (0)
2486 {
2487         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2488 }
2489
2490 void
2491 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2492 {
2493         Gdk::Cursor* cursor;
2494         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2495         assert (cnote);
2496         float x_fraction = cnote->mouse_x_fraction ();
2497
2498         if (x_fraction > 0.0 && x_fraction < 0.25) {
2499                 cursor = _editor->cursors()->left_side_trim;
2500                 at_front = true;
2501         } else  {
2502                 cursor = _editor->cursors()->right_side_trim;
2503                 at_front = false;
2504         }
2505
2506         Drag::start_grab (event, cursor);
2507
2508         region = &cnote->region_view();
2509
2510         double temp;
2511         temp = region->snap_to_pixel (cnote->x0 (), true);
2512         _snap_delta = temp - cnote->x0 ();
2513
2514         _item->grab ();
2515
2516         if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2517                 relative = false;
2518         } else {
2519                 relative = true;
2520         }
2521         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2522         if (ms.size() > 1) {
2523                 /* has to be relative, may make no sense otherwise */
2524                 relative = true;
2525         }
2526
2527         if (!(_was_selected = cnote->selected())) {
2528
2529                 /* tertiary-click means extend selection - we'll do that on button release,
2530                    so don't add it here, because otherwise we make it hard to figure
2531                    out the "extend-to" range.
2532                 */
2533
2534                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2535
2536                 if (!extend) {
2537                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2538
2539                         if (add) {
2540                                 region->note_selected (cnote, true);
2541                         } else {
2542                                 _editor->get_selection().clear_points();
2543                                 region->unique_select (cnote);
2544                         }
2545                 }
2546         }
2547 }
2548
2549 void
2550 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2551 {
2552         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2553         if (first_move) {
2554                 _editor->begin_reversible_command (_("resize notes"));
2555
2556                 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2557                         MidiRegionSelection::iterator next;
2558                         next = r;
2559                         ++next;
2560                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2561                         if (mrv) {
2562                                 mrv->begin_resizing (at_front);
2563                         }
2564                         r = next;
2565                 }
2566         }
2567
2568         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2569                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2570                 assert (nb);
2571                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2572                 if (mrv) {
2573                         double sd = 0.0;
2574                         bool snap = true;
2575                         bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2576
2577                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2578                                 if (_editor->snap_mode () != SnapOff) {
2579                                         snap = false;
2580                                 }
2581                         } else {
2582                                 if (_editor->snap_mode () == SnapOff) {
2583                                         snap = false;
2584                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2585                                         if (apply_snap_delta) {
2586                                                 snap = true;
2587                                         }
2588                                 }
2589                         }
2590
2591                         if (apply_snap_delta) {
2592                                 sd = _snap_delta;
2593                         }
2594
2595                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2596                 }
2597         }
2598 }
2599
2600 void
2601 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2602 {
2603         if (!movement_occurred) {
2604                 /* no motion - select note */
2605                 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2606                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2607                     _editor->current_mouse_mode() == Editing::MouseDraw) {
2608
2609                         bool changed = false;
2610
2611                         if (_was_selected) {
2612                                 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2613                                 if (add) {
2614                                         region->note_deselected (cnote);
2615                                         changed = true;
2616                                 } else {
2617                                         _editor->get_selection().clear_points();
2618                                         region->unique_select (cnote);
2619                                         changed = true;
2620                                 }
2621                         } else {
2622                                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2623                                 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2624
2625                                 if (!extend && !add && region->selection_size() > 1) {
2626                                         _editor->get_selection().clear_points();
2627                                         region->unique_select (cnote);
2628                                         changed = true;
2629                                 } else if (extend) {
2630                                         region->note_selected (cnote, true, true);
2631                                         changed = true;
2632                                 } else {
2633                                         /* it was added during button press */
2634                                         changed = true;
2635                                 }
2636                         }
2637
2638                         if (changed) {
2639                                 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2640                                 _editor->commit_reversible_selection_op();
2641                         }
2642                 }
2643
2644                 return;
2645         }
2646
2647         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2648         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2649                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2650                 assert (nb);
2651                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2652                 double sd = 0.0;
2653                 bool snap = true;
2654                 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2655                 if (mrv) {
2656                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2657                                 if (_editor->snap_mode () != SnapOff) {
2658                                         snap = false;
2659                                 }
2660                         } else {
2661                                 if (_editor->snap_mode () == SnapOff) {
2662                                         snap = false;
2663                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2664                                         if (apply_snap_delta) {
2665                                                 snap = true;
2666                                         }
2667                                 }
2668                         }
2669
2670                         if (apply_snap_delta) {
2671                                 sd = _snap_delta;
2672                         }
2673
2674                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2675                 }
2676         }
2677
2678         _editor->commit_reversible_command ();
2679 }
2680
2681 void
2682 NoteResizeDrag::aborted (bool)
2683 {
2684         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2685         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2686                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2687                 if (mrv) {
2688                         mrv->abort_resizing ();
2689                 }
2690         }
2691 }
2692
2693 AVDraggingView::AVDraggingView (RegionView* v)
2694         : view (v)
2695 {
2696         initial_position = v->region()->position ();
2697 }
2698
2699 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2700         : Drag (e, i)
2701 {
2702         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2703
2704         RegionSelection rs;
2705         TrackViewList empty;
2706         empty.clear();
2707         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2708         std::list<RegionView*> views = rs.by_layer();
2709
2710         _stuck = false;
2711         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2712                 RegionView* rv = (*i);
2713                 if (!rv->region()->video_locked()) {
2714                         continue;
2715                 }
2716                 if (rv->region()->locked()) {
2717                         _stuck = true;
2718                 }
2719                 _views.push_back (AVDraggingView (rv));
2720         }
2721 }
2722
2723 void
2724 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2725 {
2726         Drag::start_grab (event);
2727         if (_editor->session() == 0) {
2728                 return;
2729         }
2730
2731         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2732                 _stuck = false;
2733                 _views.clear();
2734         }
2735
2736         if (_stuck) {
2737                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2738                 return;
2739         }
2740
2741         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2742         _max_backwards_drag = (
2743                           ARDOUR_UI::instance()->video_timeline->get_duration()
2744                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2745                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2746                         );
2747
2748         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2749                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2750                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2751                 }
2752         }
2753         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2754
2755         char buf[128];
2756         Timecode::Time timecode;
2757         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2758         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);
2759         show_verbose_cursor_text (buf);
2760 }
2761
2762 void
2763 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2764 {
2765         if (_editor->session() == 0) {
2766                 return;
2767         }
2768         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2769                 return;
2770         }
2771         if (_stuck) {
2772                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2773                 return;
2774         }
2775
2776         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2777         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2778
2779         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2780                 dt = - _max_backwards_drag;
2781         }
2782
2783         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2784         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2785
2786         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2787                 RegionView* rv = i->view;
2788                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2789                 if (first_move) {
2790                         rv->drag_start ();
2791                         rv->region()->clear_changes ();
2792                         rv->region()->suspend_property_changes();
2793                 }
2794                 rv->region()->set_position(i->initial_position + dt);
2795                 rv->region_changed(ARDOUR::Properties::position);
2796         }
2797
2798         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2799         Timecode::Time timecode;
2800         Timecode::Time timediff;
2801         char buf[128];
2802         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2803         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2804         snprintf (buf, sizeof (buf),
2805                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2806                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2807                         , _("Video Start:"),
2808                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2809                         , _("Diff:"),
2810                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2811                                 );
2812         show_verbose_cursor_text (buf);
2813 }
2814
2815 void
2816 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2817 {
2818         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2819                 return;
2820         }
2821         if (_stuck) {
2822                 return;
2823         }
2824
2825         if (!movement_occurred || ! _editor->session()) {
2826                 return;
2827         }
2828
2829         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2830
2831         _editor->begin_reversible_command (_("Move Video"));
2832
2833         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2834         ARDOUR_UI::instance()->video_timeline->save_undo();
2835         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2836         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2837
2838         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2839                 i->view->drag_end();
2840                 i->view->region()->resume_property_changes ();
2841
2842                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2843         }
2844
2845         _editor->session()->maybe_update_session_range(
2846                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2847                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2848                         );
2849
2850
2851         _editor->commit_reversible_command ();
2852 }
2853
2854 void
2855 VideoTimeLineDrag::aborted (bool)
2856 {
2857         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2858                 return;
2859         }
2860         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2861         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2862
2863         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2864                 i->view->region()->resume_property_changes ();
2865                 i->view->region()->set_position(i->initial_position);
2866         }
2867 }
2868
2869 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2870         : RegionDrag (e, i, p, v)
2871         , _operation (StartTrim)
2872         , _preserve_fade_anchor (preserve_fade_anchor)
2873         , _jump_position_when_done (false)
2874 {
2875         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2876 }
2877
2878 void
2879 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2880 {
2881         double speed = 1.0;
2882         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2883         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2884
2885         if (tv && tv->is_track()) {
2886                 speed = tv->track()->speed();
2887         }
2888
2889         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2890         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2891         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2892
2893         framepos_t const pf = adjusted_current_frame (event);
2894         setup_snap_delta (MusicFrame(region_start, 0));
2895
2896         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2897                 /* Move the contents of the region around without changing the region bounds */
2898                 _operation = ContentsTrim;
2899                 Drag::start_grab (event, _editor->cursors()->trimmer);
2900         } else {
2901                 /* These will get overridden for a point trim.*/
2902                 if (pf < (region_start + region_length/2)) {
2903                         /* closer to front */
2904                         _operation = StartTrim;
2905                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2906                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2907                         } else {
2908                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2909                         }
2910                 } else {
2911                         /* closer to end */
2912                         _operation = EndTrim;
2913                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2914                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2915                         } else {
2916                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2917                         }
2918                 }
2919         }
2920         /* jump trim disabled for now
2921         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2922                 _jump_position_when_done = true;
2923         }
2924         */
2925
2926         switch (_operation) {
2927         case StartTrim:
2928                 show_verbose_cursor_time (region_start);
2929                 break;
2930         case EndTrim:
2931                 show_verbose_cursor_duration (region_start, region_end);
2932                 break;
2933         case ContentsTrim:
2934                 show_verbose_cursor_time (pf);
2935                 break;
2936         }
2937
2938         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2939                 i->view->region()->suspend_property_changes ();
2940         }
2941 }
2942
2943 void
2944 TrimDrag::motion (GdkEvent* event, bool first_move)
2945 {
2946         RegionView* rv = _primary;
2947
2948         double speed = 1.0;
2949         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2950         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2951         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2952         frameoffset_t frame_delta = 0;
2953
2954         if (tv && tv->is_track()) {
2955                 speed = tv->track()->speed();
2956         }
2957         MusicFrame adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2958         framecnt_t dt = adj_frame.frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2959
2960         if (first_move) {
2961
2962                 string trim_type;
2963
2964                 switch (_operation) {
2965                 case StartTrim:
2966                         trim_type = "Region start trim";
2967                         break;
2968                 case EndTrim:
2969                         trim_type = "Region end trim";
2970                         break;
2971                 case ContentsTrim:
2972                         trim_type = "Region content trim";
2973                         break;
2974                 default:
2975                         assert(0);
2976                         break;
2977                 }
2978
2979                 _editor->begin_reversible_command (trim_type);
2980
2981                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2982                         RegionView* rv = i->view;
2983                         rv->region()->playlist()->clear_owned_changes ();
2984
2985                         if (_operation == StartTrim) {
2986                                 rv->trim_front_starting ();
2987                         }
2988
2989                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2990
2991                         if (arv) {
2992                                 arv->temporarily_hide_envelope ();
2993                                 arv->drag_start ();
2994                         }
2995
2996                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2997                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2998
2999                         if (insert_result.second) {
3000                                 pl->freeze();
3001                         }
3002
3003                         MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (rv);
3004                         /* a MRV start trim may change the source length. ensure we cover all playlists here */
3005                         if (mrv && _operation == StartTrim) {
3006                                 vector<boost::shared_ptr<Playlist> > all_playlists;
3007                                 _editor->session()->playlists->get (all_playlists);
3008                                 for (vector<boost::shared_ptr<Playlist> >::iterator x = all_playlists.begin(); x != all_playlists.end(); ++x) {
3009
3010                                         if ((*x)->uses_source (rv->region()->source(0))) {
3011                                                 insert_result = _editor->motion_frozen_playlists.insert (*x);
3012                                                 if (insert_result.second) {
3013                                                         (*x)->clear_owned_changes ();
3014                                                         (*x)->freeze();
3015                                                 }
3016
3017                                         }
3018                                 }
3019                         }
3020                 }
3021         }
3022
3023         bool non_overlap_trim = false;
3024
3025         if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
3026                 non_overlap_trim = true;
3027         }
3028
3029         /* contstrain trim to fade length */
3030         if (_preserve_fade_anchor) {
3031                 switch (_operation) {
3032                         case StartTrim:
3033                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3034                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3035                                         if (!arv) continue;
3036                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3037                                         if (ar->locked()) continue;
3038                                         framecnt_t len = ar->fade_in()->back()->when;
3039                                         if (len < dt) dt = min(dt, len);
3040                                 }
3041                                 break;
3042                         case EndTrim:
3043                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3044                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3045                                         if (!arv) continue;
3046                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3047                                         if (ar->locked()) continue;
3048                                         framecnt_t len = ar->fade_out()->back()->when;
3049                                         if (len < -dt) dt = max(dt, -len);
3050                                 }
3051                                 break;
3052                         case ContentsTrim:
3053                                 break;
3054                 }
3055         }
3056
3057         switch (_operation) {
3058         case StartTrim:
3059                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3060                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
3061                                                             , adj_frame.division);
3062
3063                         if (changed && _preserve_fade_anchor) {
3064                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3065                                 if (arv) {
3066                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3067                                         framecnt_t len = ar->fade_in()->back()->when;
3068                                         framecnt_t diff = ar->first_frame() - i->initial_position;
3069                                         framepos_t new_length = len - diff;
3070                                         i->anchored_fade_length = min (ar->length(), new_length);
3071                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
3072                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
3073                                 }
3074                         }
3075                 }
3076                 break;
3077
3078         case EndTrim:
3079                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3080                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, adj_frame.division);
3081                         if (changed && _preserve_fade_anchor) {
3082                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3083                                 if (arv) {
3084                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3085                                         framecnt_t len = ar->fade_out()->back()->when;
3086                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
3087                                         framepos_t new_length = len + diff;
3088                                         i->anchored_fade_length = min (ar->length(), new_length);
3089                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
3090                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
3091                                 }
3092                         }
3093                 }
3094                 break;
3095
3096         case ContentsTrim:
3097                 {
3098                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
3099
3100                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3101                                 i->view->move_contents (frame_delta);
3102                         }
3103                 }
3104                 break;
3105         }
3106
3107         switch (_operation) {
3108         case StartTrim:
3109                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
3110                 break;
3111         case EndTrim:
3112                 show_verbose_cursor_duration ((framepos_t)  rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
3113                 break;
3114         case ContentsTrim:
3115                 // show_verbose_cursor_time (frame_delta);
3116                 break;
3117         }
3118 }
3119
3120 void
3121 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
3122 {
3123         if (movement_occurred) {
3124                 motion (event, false);
3125
3126                 if (_operation == StartTrim) {
3127                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3128                                 {
3129                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
3130                                            `correct' (ahem) the region's _start from being negative to being zero.  It
3131                                            needs to be zero in the undo record.
3132                                         */
3133                                         i->view->trim_front_ending ();
3134                                 }
3135                                 if (_preserve_fade_anchor) {
3136                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3137                                         if (arv) {
3138                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3139                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3140                                                 ar->set_fade_in_length(i->anchored_fade_length);
3141                                                 ar->set_fade_in_active(true);
3142                                         }
3143                                 }
3144                                 if (_jump_position_when_done) {
3145                                         i->view->region()->set_position (i->initial_position);
3146                                 }
3147                         }
3148                 } else if (_operation == EndTrim) {
3149                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3150                                 if (_preserve_fade_anchor) {
3151                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3152                                         if (arv) {
3153                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3154                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3155                                                 ar->set_fade_out_length(i->anchored_fade_length);
3156                                                 ar->set_fade_out_active(true);
3157                                         }
3158                                 }
3159                                 if (_jump_position_when_done) {
3160                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
3161                                 }
3162                         }
3163                 }
3164
3165                 if (!_editor->selection->selected (_primary)) {
3166                         _primary->thaw_after_trim ();
3167                 } else {
3168                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3169                                 i->view->thaw_after_trim ();
3170                                 i->view->enable_display (true);
3171                         }
3172                 }
3173
3174                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3175                         /* Trimming one region may affect others on the playlist, so we need
3176                            to get undo Commands from the whole playlist rather than just the
3177                            region.  Use motion_frozen_playlists (a set) to make sure we don't
3178                            diff a given playlist more than once.
3179                         */
3180
3181                         vector<Command*> cmds;
3182                         (*p)->rdiff (cmds);
3183                         _editor->session()->add_commands (cmds);
3184                         (*p)->thaw ();
3185                 }
3186
3187                 _editor->motion_frozen_playlists.clear ();
3188                 _editor->commit_reversible_command();
3189
3190         } else {
3191                 /* no mouse movement */
3192                 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false).frame) {
3193                         _editor->point_trim (event, adjusted_current_frame (event));
3194                 }
3195         }
3196
3197         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3198                 i->view->region()->resume_property_changes ();
3199         }
3200 }
3201
3202 void
3203 TrimDrag::aborted (bool movement_occurred)
3204 {
3205         /* Our motion method is changing model state, so use the Undo system
3206            to cancel.  Perhaps not ideal, as this will leave an Undo point
3207            behind which may be slightly odd from the user's point of view.
3208         */
3209
3210         GdkEvent ev;
3211         finished (&ev, true);
3212
3213         if (movement_occurred) {
3214                 _editor->session()->undo (1);
3215         }
3216
3217         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3218                 i->view->region()->resume_property_changes ();
3219         }
3220 }
3221
3222 void
3223 TrimDrag::setup_pointer_frame_offset ()
3224 {
3225         list<DraggingView>::iterator i = _views.begin ();
3226         while (i != _views.end() && i->view != _primary) {
3227                 ++i;
3228         }
3229
3230         if (i == _views.end()) {
3231                 return;
3232         }
3233
3234         switch (_operation) {
3235         case StartTrim:
3236                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3237                 break;
3238         case EndTrim:
3239                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3240                 break;
3241         case ContentsTrim:
3242                 break;
3243         }
3244 }
3245
3246 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3247         : Drag (e, i)
3248         , _copy (c)
3249         , _old_snap_type (e->snap_type())
3250         , _old_snap_mode (e->snap_mode())
3251         , before_state (0)
3252 {
3253         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3254         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3255         assert (_marker);
3256         _real_section = &_marker->meter();
3257
3258 }
3259
3260 void
3261 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3262 {
3263         Drag::start_grab (event, cursor);
3264         show_verbose_cursor_time (adjusted_current_frame(event));
3265 }
3266
3267 void
3268 MeterMarkerDrag::setup_pointer_frame_offset ()
3269 {
3270         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3271 }
3272
3273 void
3274 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3275 {
3276         if (first_move) {
3277                 // create a dummy marker to catch events, then hide it.
3278
3279                 char name[64];
3280                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3281
3282                 _marker = new MeterMarker (
3283                         *_editor,
3284                         *_editor->meter_group,
3285                         UIConfiguration::instance().color ("meter marker"),
3286                         name,
3287                         *new MeterSection (_marker->meter())
3288                 );
3289
3290                 /* use the new marker for the grab */
3291                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3292                 _marker->hide();
3293
3294                 TempoMap& map (_editor->session()->tempo_map());
3295                 /* get current state */
3296                 before_state = &map.get_state();
3297
3298                 if (!_copy) {
3299                         _editor->begin_reversible_command (_("move meter mark"));
3300                 } else {
3301                         _editor->begin_reversible_command (_("copy meter mark"));
3302
3303                         Timecode::BBT_Time bbt = _real_section->bbt();
3304
3305                         /* we can't add a meter where one currently exists */
3306                         if (_real_section->frame() < adjusted_current_frame (event, false)) {
3307                                 ++bbt.bars;
3308                         } else {
3309                                 --bbt.bars;
3310                         }
3311                         const double beat = map.beat_at_bbt (bbt);
3312                         const framepos_t frame = map.frame_at_beat (beat);
3313                         _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3314                                                        , beat, bbt, frame, _real_section->position_lock_style());
3315                         if (!_real_section) {
3316                                 aborted (true);
3317                                 return;
3318                         }
3319
3320                 }
3321                 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3322                 if (_real_section->position_lock_style() != AudioTime) {
3323                         _editor->set_snap_to (SnapToBar);
3324                         _editor->set_snap_mode (SnapNormal);
3325                 }
3326         }
3327
3328         framepos_t pf = adjusted_current_frame (event);
3329
3330         if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3331                 /* never snap to music for audio locked */
3332                 pf = adjusted_current_frame (event, false);
3333         }
3334
3335         _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3336
3337         /* fake marker meeds to stay under the mouse, unlike the real one. */
3338         _marker->set_position (adjusted_current_frame (event, false));
3339
3340         show_verbose_cursor_time (_real_section->frame());
3341 }
3342
3343 void
3344 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3345 {
3346         if (!movement_occurred) {
3347                 if (was_double_click()) {
3348                         _editor->edit_meter_marker (*_marker);
3349                 }
3350                 return;
3351         }
3352
3353         /* reinstate old snap setting */
3354         _editor->set_snap_to (_old_snap_type);
3355         _editor->set_snap_mode (_old_snap_mode);
3356
3357         TempoMap& map (_editor->session()->tempo_map());
3358
3359         XMLNode &after = map.get_state();
3360         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3361         _editor->commit_reversible_command ();
3362
3363         // delete the dummy marker we used for visual representation while moving.
3364         // a new visual marker will show up automatically.
3365         delete _marker;
3366 }
3367
3368 void
3369 MeterMarkerDrag::aborted (bool moved)
3370 {
3371         _marker->set_position (_marker->meter().frame ());
3372         if (moved) {
3373                 /* reinstate old snap setting */
3374                 _editor->set_snap_to (_old_snap_type);
3375                 _editor->set_snap_mode (_old_snap_mode);
3376
3377                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3378                 // delete the dummy marker we used for visual representation while moving.
3379                 // a new visual marker will show up automatically.
3380                 delete _marker;
3381         }
3382 }
3383
3384 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3385         : Drag (e, i)
3386         , _copy (c)
3387         , _grab_bpm (120.0, 4.0)
3388         , _grab_qn (0.0)
3389         , _before_state (0)
3390 {
3391         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3392
3393         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3394         _real_section = &_marker->tempo();
3395         _movable = !_real_section->initial();
3396         _grab_bpm = Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), _real_section->end_note_types_per_minute());
3397         _grab_qn = _real_section->pulse() * 4.0;
3398         assert (_marker);
3399 }
3400
3401 void
3402 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3403 {
3404         Drag::start_grab (event, cursor);
3405         if (!_real_section->active()) {
3406                 show_verbose_cursor_text (_("inactive"));
3407         } else {
3408                 show_verbose_cursor_time (adjusted_current_frame (event));
3409         }
3410 }
3411
3412 void
3413 TempoMarkerDrag::setup_pointer_frame_offset ()
3414 {
3415         _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3416 }
3417
3418 void
3419 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3420 {
3421         if (!_real_section->active()) {
3422                 return;
3423         }
3424         TempoMap& map (_editor->session()->tempo_map());
3425
3426         if (first_move) {
3427
3428                 // mvc drag - create a dummy marker to catch events, hide it.
3429
3430                 char name[64];
3431                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3432
3433                 TempoSection section (_marker->tempo());
3434
3435                 _marker = new TempoMarker (
3436                         *_editor,
3437                         *_editor->tempo_group,
3438                         UIConfiguration::instance().color ("tempo marker"),
3439                         name,
3440                         *new TempoSection (_marker->tempo())
3441                         );
3442
3443                 /* use the new marker for the grab */
3444                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3445                 _marker->hide();
3446
3447                 /* get current state */
3448                 _before_state = &map.get_state();
3449
3450                 if (!_copy) {
3451                         _editor->begin_reversible_command (_("move tempo mark"));
3452
3453                 } else {
3454                         const Tempo tempo (_marker->tempo());
3455                         const framepos_t frame = adjusted_current_frame (event) + 1;
3456
3457                         _editor->begin_reversible_command (_("copy tempo mark"));
3458
3459                         if (_real_section->position_lock_style() == MusicTime) {
3460                                 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3461                                 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, MusicTime);
3462                         } else {
3463                                 _real_section = map.add_tempo (tempo, 0.0, frame, AudioTime);
3464                         }
3465
3466                         if (!_real_section) {
3467                                 aborted (true);
3468                                 return;
3469                         }
3470                 }
3471
3472         }
3473         if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
3474                 double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3475                 stringstream strs;
3476                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm));
3477                 strs << "end:" << fixed << setprecision(3) << new_bpm;
3478                 show_verbose_cursor_text (strs.str());
3479
3480         } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3481                 /* use vertical movement to alter tempo .. should be log */
3482                 double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3483                 stringstream strs;
3484                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute()));
3485                 strs << "start:" << fixed << setprecision(3) << new_bpm;
3486                 show_verbose_cursor_text (strs.str());
3487
3488         } else if (_movable && !_real_section->locked_to_meter()) {
3489                 framepos_t pf;
3490
3491                 if (_editor->snap_musical()) {
3492                         /* we can't snap to a grid that we are about to move.
3493                          * gui_move_tempo() will sort out snap using the supplied beat divisions.
3494                         */
3495                         pf = adjusted_current_frame (event, false);
3496                 } else {
3497                         pf = adjusted_current_frame (event);
3498                 }
3499
3500                 /* snap to beat is 1, snap to bar is -1 (sorry) */
3501                 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3502
3503                 map.gui_set_tempo_position (_real_section, pf, sub_num);
3504
3505                 show_verbose_cursor_time (_real_section->frame());
3506         }
3507         _marker->set_position (adjusted_current_frame (event, false));
3508 }
3509
3510 void
3511 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3512 {
3513         if (!_real_section->active()) {
3514                 return;
3515         }
3516         if (!movement_occurred) {
3517                 if (was_double_click()) {
3518                         _editor->edit_tempo_marker (*_marker);
3519                 }
3520                 return;
3521         }
3522
3523         TempoMap& map (_editor->session()->tempo_map());
3524
3525         XMLNode &after = map.get_state();
3526         _editor->session()->add_command (new MementoCommand<TempoMap>(map, _before_state, &after));
3527         _editor->commit_reversible_command ();
3528
3529         // delete the dummy marker we used for visual representation while moving.
3530         // a new visual marker will show up automatically.
3531         delete _marker;
3532 }
3533
3534 void
3535 TempoMarkerDrag::aborted (bool moved)
3536 {
3537         _marker->set_position (_marker->tempo().frame());
3538         if (moved) {
3539                 TempoMap& map (_editor->session()->tempo_map());
3540                 map.set_state (*_before_state, Stateful::current_state_version);
3541                 // delete the dummy (hidden) marker we used for events while moving.
3542                 delete _marker;
3543         }
3544 }
3545
3546 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3547         : Drag (e, i)
3548         , _grab_qn (0.0)
3549         , _tempo (0)
3550         , _before_state (0)
3551 {
3552         DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3553
3554 }
3555
3556 void
3557 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3558 {
3559         Drag::start_grab (event, cursor);
3560         TempoMap& map (_editor->session()->tempo_map());
3561         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3562         _editor->tempo_curve_selected (_tempo, true);
3563
3564         ostringstream sstr;
3565         if (_tempo->clamped()) {
3566                 TempoSection* prev = map.previous_tempo_section (_tempo);
3567                 if (prev) {
3568                         _editor->tempo_curve_selected (prev, true);
3569                         sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3570                 }
3571         }
3572
3573         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3574         show_verbose_cursor_text (sstr.str());
3575 }
3576
3577 void
3578 BBTRulerDrag::setup_pointer_frame_offset ()
3579 {
3580         TempoMap& map (_editor->session()->tempo_map());
3581         /* get current state */
3582         _before_state = &map.get_state();
3583
3584         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3585         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3586         double beat = 0.0;
3587
3588         if (divisions > 0) {
3589                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3590         } else {
3591                 /* while it makes some sense for the user to determine the division to 'grab',
3592                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3593                    and the result over steep tempo curves. Use sixteenths.
3594                 */
3595                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3596         }
3597
3598         _grab_qn = map.quarter_note_at_beat (beat);
3599
3600         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3601
3602 }
3603
3604 void
3605 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3606 {
3607         TempoMap& map (_editor->session()->tempo_map());
3608
3609         if (first_move) {
3610                 _editor->begin_reversible_command (_("stretch tempo"));
3611         }
3612
3613         framepos_t pf;
3614
3615         if (_editor->snap_musical()) {
3616                 pf = adjusted_current_frame (event, false);
3617         } else {
3618                 pf = adjusted_current_frame (event);
3619         }
3620
3621         if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3622                 /* adjust previous tempo to match pointer frame */
3623                 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf);
3624         }
3625
3626         ostringstream sstr;
3627         if (_tempo->clamped()) {
3628                 TempoSection* prev = map.previous_tempo_section (_tempo);
3629                 if (prev) {
3630                         _editor->tempo_curve_selected (prev, true);
3631                         sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3632                 }
3633         }
3634         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3635         show_verbose_cursor_text (sstr.str());
3636 }
3637
3638 void
3639 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3640 {
3641         if (!movement_occurred) {
3642                 return;
3643         }
3644
3645         TempoMap& map (_editor->session()->tempo_map());
3646
3647         XMLNode &after = map.get_state();
3648         _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3649         _editor->commit_reversible_command ();
3650         _editor->tempo_curve_selected (_tempo, false);
3651
3652         if (_tempo->clamped()) {
3653                 TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
3654                 if (prev_tempo) {
3655                         _editor->tempo_curve_selected (prev_tempo, false);
3656                 }
3657         }
3658 }
3659
3660 void
3661 BBTRulerDrag::aborted (bool moved)
3662 {
3663         if (moved) {
3664                 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3665         }
3666 }
3667
3668 TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
3669         : Drag (e, i)
3670         , _grab_qn (0.0)
3671         , _grab_tempo (0.0)
3672         , _tempo (0)
3673         , _next_tempo (0)
3674         , _drag_valid (true)
3675         , _before_state (0)
3676 {
3677         DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
3678
3679 }
3680
3681 void
3682 TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3683 {
3684         Drag::start_grab (event, cursor);
3685         TempoMap& map (_editor->session()->tempo_map());
3686         /* get current state */
3687         _before_state = &map.get_state();
3688         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3689
3690         _next_tempo = map.next_tempo_section (_tempo);
3691         if (_next_tempo) {
3692                 if (!map.next_tempo_section (_next_tempo)) {
3693                         _drag_valid = false;
3694                         finished (event, false);
3695
3696                         return;
3697                 }
3698                 _editor->tempo_curve_selected (_tempo, true);
3699                 _editor->tempo_curve_selected (_next_tempo, true);
3700
3701                 ostringstream sstr;
3702                 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3703                 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3704                 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3705                 show_verbose_cursor_text (sstr.str());
3706         } else {
3707                 _drag_valid = false;
3708         }
3709
3710         _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
3711 }
3712
3713 void
3714 TempoTwistDrag::setup_pointer_frame_offset ()
3715 {
3716         TempoMap& map (_editor->session()->tempo_map());
3717         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3718         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3719         double beat = 0.0;
3720
3721         if (divisions > 0) {
3722                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3723         } else {
3724                 /* while it makes some sense for the user to determine the division to 'grab',
3725                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3726                    and the result over steep tempo curves. Use sixteenths.
3727                 */
3728                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3729         }
3730
3731         _grab_qn = map.quarter_note_at_beat (beat);
3732
3733         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3734
3735 }
3736
3737 void
3738 TempoTwistDrag::motion (GdkEvent* event, bool first_move)
3739 {
3740
3741         if (!_next_tempo || !_drag_valid) {
3742                 return;
3743         }
3744
3745         TempoMap& map (_editor->session()->tempo_map());
3746
3747         if (first_move) {
3748                 _editor->begin_reversible_command (_("twist tempo"));
3749         }
3750
3751         framepos_t pf;
3752
3753         if (_editor->snap_musical()) {
3754                 pf = adjusted_current_frame (event, false);
3755         } else {
3756                 pf = adjusted_current_frame (event);
3757         }
3758
3759         /* adjust this and the next tempi to match pointer frame */
3760         double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3761         _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
3762
3763         ostringstream sstr;
3764         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3765         sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3766         sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3767         show_verbose_cursor_text (sstr.str());
3768 }
3769
3770 void
3771 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
3772 {
3773         TempoMap& map (_editor->session()->tempo_map());
3774
3775         if (!movement_occurred || !_drag_valid) {
3776                 return;
3777         }
3778
3779         _editor->tempo_curve_selected (_tempo, false);
3780         _editor->tempo_curve_selected (_next_tempo, false);
3781
3782         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 {
3801         DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
3802         TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3803         _tempo = &marker->tempo();
3804         _grab_qn = _tempo->pulse() * 4.0;
3805 }
3806
3807 void
3808 TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3809 {
3810         Drag::start_grab (event, cursor);
3811         TempoMap& tmap (_editor->session()->tempo_map());
3812
3813         /* get current state */
3814         _before_state = &tmap.get_state();
3815
3816
3817         ostringstream sstr;
3818
3819         TempoSection* prev = 0;
3820         if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3821                 _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
3822                 sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3823         }
3824
3825         if (_tempo->clamped()) {
3826                 _editor->tempo_curve_selected (_tempo, true);
3827                 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3828         }
3829
3830         show_verbose_cursor_text (sstr.str());
3831 }
3832
3833 void
3834 TempoEndDrag::setup_pointer_frame_offset ()
3835 {
3836         TempoMap& map (_editor->session()->tempo_map());
3837
3838         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3839
3840 }
3841
3842 void
3843 TempoEndDrag::motion (GdkEvent* event, bool first_move)
3844 {
3845         TempoMap& map (_editor->session()->tempo_map());
3846
3847         if (first_move) {
3848                 _editor->begin_reversible_command (_("stretch end tempo"));
3849         }
3850
3851
3852
3853         framepos_t const pf = adjusted_current_frame (event, false);
3854         map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
3855
3856         ostringstream sstr;
3857         sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3858
3859         if (_tempo->clamped()) {
3860                 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3861         }
3862
3863         show_verbose_cursor_text (sstr.str());
3864 }
3865
3866 void
3867 TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
3868 {
3869         if (!movement_occurred) {
3870                 return;
3871         }
3872
3873         TempoMap& tmap (_editor->session()->tempo_map());
3874
3875         XMLNode &after = tmap.get_state();
3876         _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
3877         _editor->commit_reversible_command ();
3878
3879         TempoSection* prev = 0;
3880         if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3881                 _editor->tempo_curve_selected (prev, false);
3882         }
3883
3884         if (_tempo->clamped()) {
3885                 _editor->tempo_curve_selected (_tempo, false);
3886
3887         }
3888 }
3889
3890 void
3891 TempoEndDrag::aborted (bool moved)
3892 {
3893         if (moved) {
3894                 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3895         }
3896 }
3897
3898 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3899         : Drag (e, &c.track_canvas_item(), false)
3900         , _cursor (c)
3901         , _stop (s)
3902         , _grab_zoom (0.0)
3903 {
3904         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3905 }
3906
3907 /** Do all the things we do when dragging the playhead to make it look as though
3908  *  we have located, without actually doing the locate (because that would cause
3909  *  the diskstream buffers to be refilled, which is too slow).
3910  */
3911 void
3912 CursorDrag::fake_locate (framepos_t t)
3913 {
3914         if (_editor->session () == 0) {
3915                 return;
3916         }
3917
3918         _editor->playhead_cursor->set_position (t);
3919
3920         Session* s = _editor->session ();
3921         if (s->timecode_transmission_suspended ()) {
3922                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3923                 /* This is asynchronous so it will be sent "now"
3924                  */
3925                 s->send_mmc_locate (f);
3926                 /* These are synchronous and will be sent during the next
3927                    process cycle
3928                 */
3929                 s->queue_full_time_code ();
3930                 s->queue_song_position_pointer ();
3931         }
3932
3933         show_verbose_cursor_time (t);
3934         _editor->UpdateAllTransportClocks (t);
3935 }
3936
3937 void
3938 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3939 {
3940         Drag::start_grab (event, c);
3941         setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
3942
3943         _grab_zoom = _editor->samples_per_pixel;
3944
3945         MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3946
3947         _editor->snap_to_with_modifier (where, event);
3948         _editor->_dragging_playhead = true;
3949         _editor->_control_scroll_target = where.frame;
3950
3951         Session* s = _editor->session ();
3952
3953         /* grab the track canvas item as well */
3954
3955         _cursor.track_canvas_item().grab();
3956
3957         if (s) {
3958                 if (_was_rolling && _stop) {
3959                         s->request_stop ();
3960                 }
3961
3962                 if (s->is_auditioning()) {
3963                         s->cancel_audition ();
3964                 }
3965
3966
3967                 if (AudioEngine::instance()->running()) {
3968
3969                         /* do this only if we're the engine is connected
3970                          * because otherwise this request will never be
3971                          * serviced and we'll busy wait forever. likewise,
3972                          * notice if we are disconnected while waiting for the
3973                          * request to be serviced.
3974                          */
3975
3976                         s->request_suspend_timecode_transmission ();
3977                         while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
3978                                 /* twiddle our thumbs */
3979                         }
3980                 }
3981         }
3982
3983         fake_locate (where.frame - snap_delta (event->button.state));
3984 }
3985
3986 void
3987 CursorDrag::motion (GdkEvent* event, bool)
3988 {
3989         MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3990
3991         _editor->snap_to_with_modifier (where, event);
3992
3993         if (where.frame != last_pointer_frame()) {
3994                 fake_locate (where.frame - snap_delta (event->button.state));
3995         }
3996 }
3997
3998 void
3999 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
4000 {
4001         _editor->_dragging_playhead = false;
4002
4003         _cursor.track_canvas_item().ungrab();
4004
4005         if (!movement_occurred && _stop) {
4006                 return;
4007         }
4008
4009         motion (event, false);
4010
4011         Session* s = _editor->session ();
4012         if (s) {
4013                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
4014                 _editor->_pending_locate_request = true;
4015                 s->request_resume_timecode_transmission ();
4016         }
4017 }
4018
4019 void
4020 CursorDrag::aborted (bool)
4021 {
4022         _cursor.track_canvas_item().ungrab();
4023
4024         if (_editor->_dragging_playhead) {
4025                 _editor->session()->request_resume_timecode_transmission ();
4026                 _editor->_dragging_playhead = false;
4027         }
4028
4029         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
4030 }
4031
4032 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4033         : RegionDrag (e, i, p, v)
4034 {
4035         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
4036 }
4037
4038 void
4039 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4040 {
4041         Drag::start_grab (event, cursor);
4042
4043         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4044         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4045         setup_snap_delta (MusicFrame (r->position(), 0));
4046
4047         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
4048 }
4049
4050 void
4051 FadeInDrag::setup_pointer_frame_offset ()
4052 {
4053         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4054         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4055         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
4056 }
4057
4058 void
4059 FadeInDrag::motion (GdkEvent* event, bool)
4060 {
4061         framecnt_t fade_length;
4062
4063         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4064         _editor->snap_to_with_modifier (pos, event);
4065
4066         pos.frame -= snap_delta (event->button.state);
4067
4068         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4069
4070         if (pos.frame < (region->position() + 64)) {
4071                 fade_length = 64; // this should be a minimum defined somewhere
4072         } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
4073                 fade_length = region->length() - region->fade_out()->back()->when - 1;
4074         } else {
4075                 fade_length = pos.frame - region->position();
4076         }
4077
4078         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4079
4080                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4081
4082                 if (!tmp) {
4083                         continue;
4084                 }
4085
4086                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
4087         }
4088
4089         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
4090 }
4091
4092 void
4093 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
4094 {
4095         if (!movement_occurred) {
4096                 return;
4097         }
4098
4099         framecnt_t fade_length;
4100         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4101
4102         _editor->snap_to_with_modifier (pos, event);
4103         pos.frame -= snap_delta (event->button.state);
4104
4105         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4106
4107         if (pos.frame < (region->position() + 64)) {
4108                 fade_length = 64; // this should be a minimum defined somewhere
4109         } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
4110                 fade_length = region->length() - region->fade_out()->back()->when - 1;
4111         } else {
4112                 fade_length = pos.frame - region->position();
4113         }
4114
4115         bool in_command = false;
4116
4117         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4118
4119                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4120
4121                 if (!tmp) {
4122                         continue;
4123                 }
4124
4125                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4126                 XMLNode &before = alist->get_state();
4127
4128                 tmp->audio_region()->set_fade_in_length (fade_length);
4129                 tmp->audio_region()->set_fade_in_active (true);
4130
4131                 if (!in_command) {
4132                         _editor->begin_reversible_command (_("change fade in length"));
4133                         in_command = true;
4134                 }
4135                 XMLNode &after = alist->get_state();
4136                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4137         }
4138
4139         if (in_command) {
4140                 _editor->commit_reversible_command ();
4141         }
4142 }
4143
4144 void
4145 FadeInDrag::aborted (bool)
4146 {
4147         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4148                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4149
4150                 if (!tmp) {
4151                         continue;
4152                 }
4153
4154                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
4155         }
4156 }
4157
4158 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4159         : RegionDrag (e, i, p, v)
4160 {
4161         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
4162 }
4163
4164 void
4165 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4166 {
4167         Drag::start_grab (event, cursor);
4168
4169         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4170         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4171         setup_snap_delta (MusicFrame (r->last_frame(), 0));
4172
4173         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
4174 }
4175
4176 void
4177 FadeOutDrag::setup_pointer_frame_offset ()
4178 {
4179         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4180         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4181         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
4182 }
4183
4184 void
4185 FadeOutDrag::motion (GdkEvent* event, bool)
4186 {
4187         framecnt_t fade_length;
4188         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4189
4190         _editor->snap_to_with_modifier (pos, event);
4191         pos.frame -= snap_delta (event->button.state);
4192
4193         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4194
4195         if (pos.frame > (region->last_frame() - 64)) {
4196                 fade_length = 64; // this should really be a minimum fade defined somewhere
4197         } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4198                 fade_length = region->length() - region->fade_in()->back()->when - 1;
4199         } else {
4200                 fade_length = region->last_frame() - pos.frame;
4201         }
4202
4203         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4204
4205                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4206
4207                 if (!tmp) {
4208                         continue;
4209                 }
4210
4211                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
4212         }
4213
4214         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
4215 }
4216
4217 void
4218 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
4219 {
4220         if (!movement_occurred) {
4221                 return;
4222         }
4223
4224         framecnt_t fade_length;
4225         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4226
4227         _editor->snap_to_with_modifier (pos, event);
4228         pos.frame -= snap_delta (event->button.state);
4229
4230         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4231
4232         if (pos.frame > (region->last_frame() - 64)) {
4233                 fade_length = 64; // this should really be a minimum fade defined somewhere
4234         } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4235                 fade_length = region->length() - region->fade_in()->back()->when - 1;
4236         } else {
4237                 fade_length = region->last_frame() - pos.frame;
4238         }
4239
4240         bool in_command = false;
4241
4242         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4243
4244                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4245
4246                 if (!tmp) {
4247                         continue;
4248                 }
4249
4250                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4251                 XMLNode &before = alist->get_state();
4252
4253                 tmp->audio_region()->set_fade_out_length (fade_length);
4254                 tmp->audio_region()->set_fade_out_active (true);
4255
4256                 if (!in_command) {
4257                         _editor->begin_reversible_command (_("change fade out length"));
4258                         in_command = true;
4259                 }
4260                 XMLNode &after = alist->get_state();
4261                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4262         }
4263
4264         if (in_command) {
4265                 _editor->commit_reversible_command ();
4266         }
4267 }
4268
4269 void
4270 FadeOutDrag::aborted (bool)
4271 {
4272         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4273                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4274
4275                 if (!tmp) {
4276                         continue;
4277                 }
4278
4279                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4280         }
4281 }
4282
4283 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4284         : Drag (e, i)
4285         , _selection_changed (false)
4286 {
4287         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4288         Gtk::Window* toplevel = _editor->current_toplevel();
4289         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4290
4291         assert (_marker);
4292
4293         _points.push_back (ArdourCanvas::Duple (0, 0));
4294
4295         _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4296 }
4297
4298 MarkerDrag::~MarkerDrag ()
4299 {
4300         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4301                 delete i->location;
4302         }
4303 }
4304
4305 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4306 {
4307         location = new Location (*l);
4308         markers.push_back (m);
4309         move_both = false;
4310 }
4311
4312 void
4313 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4314 {
4315         Drag::start_grab (event, cursor);
4316
4317         bool is_start;
4318
4319         Location *location = _editor->find_location_from_marker (_marker, is_start);
4320         _editor->_dragging_edit_point = true;
4321
4322         update_item (location);
4323
4324         // _drag_line->show();
4325         // _line->raise_to_top();
4326
4327         if (is_start) {
4328                 show_verbose_cursor_time (location->start());
4329         } else {
4330                 show_verbose_cursor_time (location->end());
4331         }
4332         setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
4333
4334         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4335
4336         switch (op) {
4337         case Selection::Toggle:
4338                 /* we toggle on the button release */
4339                 break;
4340         case Selection::Set:
4341                 if (!_editor->selection->selected (_marker)) {
4342                         _editor->selection->set (_marker);
4343                         _selection_changed = true;
4344                 }
4345                 break;
4346         case Selection::Extend:
4347         {
4348                 Locations::LocationList ll;
4349                 list<ArdourMarker*> to_add;
4350                 framepos_t s, e;
4351                 _editor->selection->markers.range (s, e);
4352                 s = min (_marker->position(), s);
4353                 e = max (_marker->position(), e);
4354                 s = min (s, e);
4355                 e = max (s, e);
4356                 if (e < max_framepos) {
4357                         ++e;
4358                 }
4359                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4360                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4361                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4362                         if (lm) {
4363                                 if (lm->start) {
4364                                         to_add.push_back (lm->start);
4365                                 }
4366                                 if (lm->end) {
4367                                         to_add.push_back (lm->end);
4368                                 }
4369                         }
4370                 }
4371                 if (!to_add.empty()) {
4372                         _editor->selection->add (to_add);
4373                         _selection_changed = true;
4374                 }
4375                 break;
4376         }
4377         case Selection::Add:
4378                 _editor->selection->add (_marker);
4379                 _selection_changed = true;
4380
4381                 break;
4382         }
4383
4384         /* Set up copies for us to manipulate during the drag
4385          */
4386
4387         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4388
4389                 Location* l = _editor->find_location_from_marker (*i, is_start);
4390
4391                 if (!l) {
4392                         continue;
4393                 }
4394
4395                 if (l->is_mark()) {
4396                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4397                 } else {
4398                         /* range: check that the other end of the range isn't
4399                            already there.
4400                         */
4401                         CopiedLocationInfo::iterator x;
4402                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4403                                 if (*(*x).location == *l) {
4404                                         break;
4405                                 }
4406                         }
4407                         if (x == _copied_locations.end()) {
4408                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4409                         } else {
4410                                 (*x).markers.push_back (*i);
4411                                 (*x).move_both = true;
4412                         }
4413                 }
4414
4415         }
4416 }
4417
4418 void
4419 MarkerDrag::setup_pointer_frame_offset ()
4420 {
4421         bool is_start;
4422         Location *location = _editor->find_location_from_marker (_marker, is_start);
4423         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4424 }
4425
4426 void
4427 MarkerDrag::motion (GdkEvent* event, bool)
4428 {
4429         framecnt_t f_delta = 0;
4430         bool is_start;
4431         bool move_both = false;
4432         Location *real_location;
4433         Location *copy_location = 0;
4434         framecnt_t const sd = snap_delta (event->button.state);
4435
4436         framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4437         framepos_t next = newframe;
4438
4439         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4440                 move_both = true;
4441         }
4442
4443         CopiedLocationInfo::iterator x;
4444
4445         /* find the marker we're dragging, and compute the delta */
4446
4447         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4448
4449                 copy_location = (*x).location;
4450
4451                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4452
4453                         /* this marker is represented by this
4454                          * CopiedLocationMarkerInfo
4455                          */
4456
4457                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4458                                 /* que pasa ?? */
4459                                 return;
4460                         }
4461
4462                         if (real_location->is_mark()) {
4463                                 f_delta = newframe - copy_location->start();
4464                         } else {
4465
4466
4467                                 switch (_marker->type()) {
4468                                 case ArdourMarker::SessionStart:
4469                                 case ArdourMarker::RangeStart:
4470                                 case ArdourMarker::LoopStart:
4471                                 case ArdourMarker::PunchIn:
4472                                         f_delta = newframe - copy_location->start();
4473                                         break;
4474
4475                                 case ArdourMarker::SessionEnd:
4476                                 case ArdourMarker::RangeEnd:
4477                                 case ArdourMarker::LoopEnd:
4478                                 case ArdourMarker::PunchOut:
4479                                         f_delta = newframe - copy_location->end();
4480                                         break;
4481                                 default:
4482                                         /* what kind of marker is this ? */
4483                                         return;
4484                                 }
4485                         }
4486
4487                         break;
4488                 }
4489         }
4490
4491         if (x == _copied_locations.end()) {
4492                 /* hmm, impossible - we didn't find the dragged marker */
4493                 return;
4494         }
4495
4496         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4497
4498         /* now move them all */
4499
4500         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4501
4502                 copy_location = x->location;
4503
4504                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4505                         continue;
4506                 }
4507
4508                 if (real_location->locked()) {
4509                         continue;
4510                 }
4511
4512                 if (copy_location->is_mark()) {
4513
4514                         /* now move it */
4515                         copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4516
4517                 } else {
4518
4519                         framepos_t new_start = copy_location->start() + f_delta;
4520                         framepos_t new_end = copy_location->end() + f_delta;
4521
4522                         if (is_start) { // start-of-range marker
4523
4524                                 if (move_both || (*x).move_both) {
4525                                         copy_location->set_start (new_start, false, true, divisions);
4526                                         copy_location->set_end (new_end, false, true, divisions);
4527                                 } else  if (new_start < copy_location->end()) {
4528                                         copy_location->set_start (new_start, false, true, divisions);
4529                                 } else if (newframe > 0) {
4530                                         //_editor->snap_to (next, RoundUpAlways, true);
4531                                         copy_location->set_end (next, false, true, divisions);
4532                                         copy_location->set_start (newframe, false, true, divisions);
4533                                 }
4534
4535                         } else { // end marker
4536
4537                                 if (move_both || (*x).move_both) {
4538                                         copy_location->set_end (new_end, divisions);
4539                                         copy_location->set_start (new_start, false, true, divisions);
4540                                 } else if (new_end > copy_location->start()) {
4541                                         copy_location->set_end (new_end, false, true, divisions);
4542                                 } else if (newframe > 0) {
4543                                         //_editor->snap_to (next, RoundDownAlways, true);
4544                                         copy_location->set_start (next, false, true, divisions);
4545                                         copy_location->set_end (newframe, false, true, divisions);
4546                                 }
4547                         }
4548                 }
4549
4550                 update_item (copy_location);
4551
4552                 /* now lookup the actual GUI items used to display this
4553                  * location and move them to wherever the copy of the location
4554                  * is now. This means that the logic in ARDOUR::Location is
4555                  * still enforced, even though we are not (yet) modifying
4556                  * the real Location itself.
4557                  */
4558
4559                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4560
4561                 if (lm) {
4562                         lm->set_position (copy_location->start(), copy_location->end());
4563                 }
4564
4565         }
4566
4567         assert (!_copied_locations.empty());
4568
4569         show_verbose_cursor_time (newframe);
4570 }
4571
4572 void
4573 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4574 {
4575         if (!movement_occurred) {
4576
4577                 if (was_double_click()) {
4578                         _editor->rename_marker (_marker);
4579                         return;
4580                 }
4581
4582                 /* just a click, do nothing but finish
4583                    off the selection process
4584                 */
4585
4586                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4587                 switch (op) {
4588                 case Selection::Set:
4589                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4590                                 _editor->selection->set (_marker);
4591                                 _selection_changed = true;
4592                         }
4593                         break;
4594
4595                 case Selection::Toggle:
4596                         /* we toggle on the button release, click only */
4597                         _editor->selection->toggle (_marker);
4598                         _selection_changed = true;
4599
4600                         break;
4601
4602                 case Selection::Extend:
4603                 case Selection::Add:
4604                         break;
4605                 }
4606
4607                 if (_selection_changed) {
4608                         _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4609                         _editor->commit_reversible_selection_op();
4610                 }
4611
4612                 return;
4613         }
4614
4615         _editor->_dragging_edit_point = false;
4616
4617         XMLNode &before = _editor->session()->locations()->get_state();
4618         bool in_command = false;
4619
4620         MarkerSelection::iterator i;
4621         CopiedLocationInfo::iterator x;
4622         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4623         bool is_start;
4624
4625         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4626              x != _copied_locations.end() && i != _editor->selection->markers.end();
4627              ++i, ++x) {
4628
4629                 Location * location = _editor->find_location_from_marker (*i, is_start);
4630
4631                 if (location) {
4632
4633                         if (location->locked()) {
4634                                 continue;
4635                         }
4636                         if (!in_command) {
4637                                 _editor->begin_reversible_command ( _("move marker") );
4638                                 in_command = true;
4639                         }
4640                         if (location->is_mark()) {
4641                                 location->set_start (((*x).location)->start(), false, true, divisions);
4642                         } else {
4643                                 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4644                         }
4645
4646                         if (location->is_session_range()) {
4647                                 _editor->session()->set_end_is_free (false);
4648                         }
4649                 }
4650         }
4651
4652         if (in_command) {
4653                 XMLNode &after = _editor->session()->locations()->get_state();
4654                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4655                 _editor->commit_reversible_command ();
4656         }
4657 }
4658
4659 void
4660 MarkerDrag::aborted (bool movement_occurred)
4661 {
4662         if (!movement_occurred) {
4663                 return;
4664         }
4665
4666         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4667
4668                 /* move all markers to their original location */
4669
4670
4671                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4672
4673                         bool is_start;
4674                         Location * location = _editor->find_location_from_marker (*m, is_start);
4675
4676                         if (location) {
4677                                 (*m)->set_position (is_start ? location->start() : location->end());
4678                         }
4679                 }
4680         }
4681 }
4682
4683 void
4684 MarkerDrag::update_item (Location*)
4685 {
4686         /* noop */
4687 }
4688
4689 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4690         : Drag (e, i)
4691         , _fixed_grab_x (0.0)
4692         , _fixed_grab_y (0.0)
4693         , _cumulative_x_drag (0.0)
4694         , _cumulative_y_drag (0.0)
4695         , _pushing (false)
4696         , _final_index (0)
4697 {
4698         if (_zero_gain_fraction < 0.0) {
4699                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4700         }
4701
4702         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4703
4704         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4705         assert (_point);
4706 }
4707
4708
4709 void
4710 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4711 {
4712         Drag::start_grab (event, _editor->cursors()->fader);
4713
4714         // start the grab at the center of the control point so
4715         // the point doesn't 'jump' to the mouse after the first drag
4716         _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4717         _fixed_grab_y = _point->get_y();
4718
4719         setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
4720
4721         float const fraction = 1 - (_point->get_y() / _point->line().height());
4722         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4723
4724         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4725
4726         if (!_point->can_slide ()) {
4727                 _x_constrained = true;
4728         }
4729 }
4730
4731 void
4732 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4733 {
4734         double dx = _drags->current_pointer_x() - last_pointer_x();
4735         double dy = current_pointer_y() - last_pointer_y();
4736         bool need_snap = true;
4737
4738         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4739                 dx *= 0.1;
4740                 dy *= 0.1;
4741                 need_snap = false;
4742         }
4743
4744         /* coordinate in pixels relative to the start of the region (for region-based automation)
4745            or track (for track-based automation) */
4746         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4747         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4748
4749         // calculate zero crossing point. back off by .01 to stay on the
4750         // positive side of zero
4751         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4752
4753         if (_x_constrained) {
4754                 cx = _fixed_grab_x;
4755         }
4756         if (_y_constrained) {
4757                 cy = _fixed_grab_y;
4758         }
4759
4760         _cumulative_x_drag = cx - _fixed_grab_x;
4761         _cumulative_y_drag = cy - _fixed_grab_y;
4762
4763         cx = max (0.0, cx);
4764         cy = max (0.0, cy);
4765         cy = min ((double) _point->line().height(), cy);
4766
4767         // make sure we hit zero when passing through
4768         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4769                 cy = zero_gain_y;
4770         }
4771
4772         MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4773
4774         if (!_x_constrained && need_snap) {
4775                 _editor->snap_to_with_modifier (cx_mf, event);
4776         }
4777
4778         cx_mf.frame -= snap_delta (event->button.state);
4779         cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4780
4781         float const fraction = 1.0 - (cy / _point->line().height());
4782
4783         if (first_motion) {
4784                 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4785                 _editor->begin_reversible_command (_("automation event move"));
4786                 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4787         }
4788         pair<float, float> result;
4789         result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4790         show_verbose_cursor_text (_point->line().get_verbose_cursor_relative_string (result.first, result.second));
4791 }
4792
4793 void
4794 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4795 {
4796         if (!movement_occurred) {
4797
4798                 /* just a click */
4799                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4800                         _editor->reset_point_selection ();
4801                 }
4802
4803         } else {
4804                 _point->line().end_drag (_pushing, _final_index);
4805                 _editor->commit_reversible_command ();
4806         }
4807 }
4808
4809 void
4810 ControlPointDrag::aborted (bool)
4811 {
4812         _point->line().reset ();
4813 }
4814
4815 bool
4816 ControlPointDrag::active (Editing::MouseMode m)
4817 {
4818         if (m == Editing::MouseDraw) {
4819                 /* always active in mouse draw */
4820                 return true;
4821         }
4822
4823         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4824         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4825 }
4826
4827 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4828         : Drag (e, i)
4829         , _line (0)
4830         , _fixed_grab_x (0.0)
4831         , _fixed_grab_y (0.0)
4832         , _cumulative_y_drag (0)
4833         , _before (0)
4834         , _after (0)
4835 {
4836         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4837 }
4838
4839 void
4840 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4841 {
4842         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4843         assert (_line);
4844
4845         _item = &_line->grab_item ();
4846
4847         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4848            origin, and ditto for y.
4849         */
4850
4851         double mx = event->button.x;
4852         double my = event->button.y;
4853
4854         _line->grab_item().canvas_to_item (mx, my);
4855
4856         framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4857
4858         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4859                 /* no adjacent points */
4860                 return;
4861         }
4862
4863         Drag::start_grab (event, _editor->cursors()->fader);
4864
4865         /* store grab start in item frame */
4866         double const bx = _line->nth (_before)->get_x();
4867         double const ax = _line->nth (_after)->get_x();
4868         double const click_ratio = (ax - mx) / (ax - bx);
4869
4870         double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4871
4872         _fixed_grab_x = mx;
4873         _fixed_grab_y = cy;
4874
4875         double fraction = 1.0 - (cy / _line->height());
4876
4877         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4878 }
4879
4880 void
4881 LineDrag::motion (GdkEvent* event, bool first_move)
4882 {
4883         double dy = current_pointer_y() - last_pointer_y();
4884
4885         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4886                 dy *= 0.1;
4887         }
4888
4889         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4890
4891         _cumulative_y_drag = cy - _fixed_grab_y;
4892
4893         cy = max (0.0, cy);
4894         cy = min ((double) _line->height(), cy);
4895
4896         double const fraction = 1.0 - (cy / _line->height());
4897         uint32_t ignored;
4898
4899         if (first_move) {
4900                 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4901
4902                 _editor->begin_reversible_command (_("automation range move"));
4903                 _line->start_drag_line (_before, _after, initial_fraction);
4904         }
4905
4906         /* we are ignoring x position for this drag, so we can just pass in anything */
4907         pair<float, float> result;
4908
4909         result = _line->drag_motion (0, fraction, true, false, ignored);
4910         show_verbose_cursor_text (_line->get_verbose_cursor_relative_string (result.first, result.second));
4911 }
4912
4913 void
4914 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4915 {
4916         if (movement_occurred) {
4917                 motion (event, false);
4918                 _line->end_drag (false, 0);
4919                 _editor->commit_reversible_command ();
4920         } else {
4921                 /* add a new control point on the line */
4922
4923                 AutomationTimeAxisView* atv;
4924
4925                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4926                         framepos_t where = grab_frame ();
4927
4928                         double cx = 0;
4929                         double cy = _fixed_grab_y;
4930
4931                         _line->grab_item().item_to_canvas (cx, cy);
4932
4933                         atv->add_automation_event (event, where, cy, false);
4934                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4935                         AudioRegionView* arv;
4936
4937                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4938                                 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4939                         }
4940                 }
4941         }
4942 }
4943
4944 void
4945 LineDrag::aborted (bool)
4946 {
4947         _line->reset ();
4948 }
4949
4950 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4951         : Drag (e, i),
4952           _line (0),
4953           _arv (0),
4954           _region_view_grab_x (0.0),
4955           _cumulative_x_drag (0),
4956           _before (0.0),
4957           _max_x (0)
4958 {
4959         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4960 }
4961
4962 void
4963 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4964 {
4965         Drag::start_grab (event);
4966
4967         _line = reinterpret_cast<Line*> (_item);
4968         assert (_line);
4969
4970         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4971
4972         double cx = event->button.x;
4973         double cy = event->button.y;
4974
4975         _item->parent()->canvas_to_item (cx, cy);
4976
4977         /* store grab start in parent frame */
4978         _region_view_grab_x = cx;
4979
4980         _before = *(float*) _item->get_data ("position");
4981
4982         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4983
4984         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4985 }
4986
4987 void
4988 FeatureLineDrag::motion (GdkEvent*, bool)
4989 {
4990         double dx = _drags->current_pointer_x() - last_pointer_x();
4991
4992         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4993
4994         _cumulative_x_drag += dx;
4995
4996         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4997
4998         if (cx > _max_x){
4999                 cx = _max_x;
5000         }
5001         else if(cx < 0){
5002                 cx = 0;
5003         }
5004
5005         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
5006         assert (bbox);
5007         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
5008
5009         float *pos = new float;
5010         *pos = cx;
5011
5012         _line->set_data ("position", pos);
5013
5014         _before = cx;
5015 }
5016
5017 void
5018 FeatureLineDrag::finished (GdkEvent*, bool)
5019 {
5020         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
5021         _arv->update_transient(_before, _before);
5022 }
5023
5024 void
5025 FeatureLineDrag::aborted (bool)
5026 {
5027         //_line->reset ();
5028 }
5029
5030 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5031         : Drag (e, i)
5032         , _vertical_only (false)
5033 {
5034         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
5035 }
5036
5037 void
5038 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5039 {
5040         Drag::start_grab (event);
5041         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
5042 }
5043
5044 void
5045 RubberbandSelectDrag::motion (GdkEvent* event, bool)
5046 {
5047         framepos_t start;
5048         framepos_t end;
5049         double y1;
5050         double y2;
5051         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
5052         MusicFrame grab (grab_frame (), 0);
5053
5054         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5055                 _editor->snap_to_with_modifier (grab, event);
5056         } else {
5057                 grab.frame = raw_grab_frame ();
5058         }
5059
5060         /* base start and end on initial click position */
5061
5062         if (pf < grab.frame) {
5063                 start = pf;
5064                 end = grab.frame;
5065         } else {
5066                 end = pf;
5067                 start = grab.frame;
5068         }
5069
5070         if (current_pointer_y() < grab_y()) {
5071                 y1 = current_pointer_y();
5072                 y2 = grab_y();
5073         } else {
5074                 y2 = current_pointer_y();
5075                 y1 = grab_y();
5076         }
5077
5078         if (start != end || y1 != y2) {
5079
5080                 double x1 = _editor->sample_to_pixel (start);
5081                 double x2 = _editor->sample_to_pixel (end);
5082                 const double min_dimension = 2.0;
5083
5084                 if (_vertical_only) {
5085                         /* fixed 10 pixel width */
5086                         x2 = x1 + 10;
5087                 } else {
5088                         if (x2 < x1) {
5089                                 x2 = min (x1 - min_dimension, x2);
5090                         } else {
5091                                 x2 = max (x1 + min_dimension, x2);
5092                         }
5093                 }
5094
5095                 if (y2 < y1) {
5096                         y2 = min (y1 - min_dimension, y2);
5097                 } else {
5098                         y2 = max (y1 + min_dimension, y2);
5099                 }
5100
5101                 /* translate rect into item space and set */
5102
5103                 ArdourCanvas::Rect r (x1, y1, x2, y2);
5104
5105                 /* this drag is a _trackview_only == true drag, so the y1 and
5106                  * y2 (computed using current_pointer_y() and grab_y()) will be
5107                  * relative to the top of the trackview group). The
5108                  * rubberband rect has the same parent/scroll offset as the
5109                  * the trackview group, so we can use the "r" rect directly
5110                  * to set the shape of the rubberband.
5111                  */
5112
5113                 _editor->rubberband_rect->set (r);
5114                 _editor->rubberband_rect->show();
5115                 _editor->rubberband_rect->raise_to_top();
5116
5117                 show_verbose_cursor_time (pf);
5118
5119                 do_select_things (event, true);
5120         }
5121 }
5122
5123 void
5124 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
5125 {
5126         framepos_t x1;
5127         framepos_t x2;
5128         framepos_t grab = grab_frame ();
5129         framepos_t lpf = last_pointer_frame ();
5130
5131         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5132                 grab = raw_grab_frame ();
5133                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
5134         }
5135
5136         if (grab < lpf) {
5137                 x1 = grab;
5138                 x2 = lpf;
5139         } else {
5140                 x2 = grab;
5141                 x1 = lpf;
5142         }
5143
5144         double y1;
5145         double y2;
5146
5147         if (current_pointer_y() < grab_y()) {
5148                 y1 = current_pointer_y();
5149                 y2 = grab_y();
5150         } else {
5151                 y2 = current_pointer_y();
5152                 y1 = grab_y();
5153         }
5154
5155         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
5156 }
5157
5158 void
5159 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
5160 {
5161         if (movement_occurred) {
5162
5163                 motion (event, false);
5164                 do_select_things (event, false);
5165
5166         } else {
5167
5168                 /* just a click */
5169
5170                 bool do_deselect = true;
5171                 MidiTimeAxisView* mtv;
5172
5173                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
5174                         /* MIDI track */
5175                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
5176                                 /* nothing selected */
5177                                 add_midi_region (mtv, true);
5178                                 do_deselect = false;
5179                         }
5180                 }
5181
5182                 /* do not deselect if Primary or Tertiary (toggle-select or
5183                  * extend-select are pressed.
5184                  */
5185
5186                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
5187                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
5188                     do_deselect) {
5189                         deselect_things ();
5190                 }
5191
5192         }
5193
5194         _editor->rubberband_rect->hide();
5195 }
5196
5197 void
5198 RubberbandSelectDrag::aborted (bool)
5199 {
5200         _editor->rubberband_rect->hide ();
5201 }
5202
5203 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
5204         : RegionDrag (e, i, p, v)
5205 {
5206         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
5207 }
5208
5209 void
5210 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5211 {
5212         Drag::start_grab (event, cursor);
5213
5214         _editor->get_selection().add (_primary);
5215
5216         MusicFrame where (_primary->region()->position(), 0);
5217         setup_snap_delta (where);
5218
5219         show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
5220 }
5221
5222 void
5223 TimeFXDrag::motion (GdkEvent* event, bool)
5224 {
5225         RegionView* rv = _primary;
5226         StreamView* cv = rv->get_time_axis_view().view ();
5227         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
5228         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
5229         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
5230         MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
5231
5232         _editor->snap_to_with_modifier (pf, event);
5233         pf.frame -= snap_delta (event->button.state);
5234
5235         if (pf.frame > rv->region()->position()) {
5236                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
5237         }
5238
5239         show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
5240 }
5241
5242 void
5243 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
5244 {
5245         /* this may have been a single click, no drag. We still want the dialog
5246            to show up in that case, so that the user can manually edit the
5247            parameters for the timestretch.
5248         */
5249
5250         float fraction = 1.0;
5251
5252         if (movement_occurred) {
5253
5254                 motion (event, false);
5255
5256                 _primary->get_time_axis_view().hide_timestretch ();
5257
5258                 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
5259
5260                 if (adjusted_frame_pos < _primary->region()->position()) {
5261                         /* backwards drag of the left edge - not usable */
5262                         return;
5263                 }
5264
5265                 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
5266
5267                 fraction = (double) newlen / (double) _primary->region()->length();
5268
5269 #ifndef USE_RUBBERBAND
5270                 // Soundtouch uses fraction / 100 instead of normal (/ 1)
5271                 if (_primary->region()->data_type() == DataType::AUDIO) {
5272                         fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
5273                 }
5274 #endif
5275         }
5276
5277         if (!_editor->get_selection().regions.empty()) {
5278                 /* primary will already be included in the selection, and edit
5279                    group shared editing will propagate selection across
5280                    equivalent regions, so just use the current region
5281                    selection.
5282                 */
5283
5284                 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5285                         error << _("An error occurred while executing time stretch operation") << endmsg;
5286                 }
5287         }
5288 }
5289
5290 void
5291 TimeFXDrag::aborted (bool)
5292 {
5293         _primary->get_time_axis_view().hide_timestretch ();
5294 }
5295
5296 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5297         : Drag (e, i)
5298 {
5299         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5300 }
5301
5302 void
5303 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5304 {
5305         Drag::start_grab (event);
5306 }
5307
5308 void
5309 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5310 {
5311         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5312 }
5313
5314 void
5315 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5316 {
5317         if (movement_occurred && _editor->session()) {
5318                 /* make sure we stop */
5319                 _editor->session()->request_transport_speed (0.0);
5320         }
5321 }
5322
5323 void
5324 ScrubDrag::aborted (bool)
5325 {
5326         /* XXX: TODO */
5327 }
5328
5329 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5330         : Drag (e, i)
5331         , _operation (o)
5332         , _add (false)
5333         , _track_selection_at_start (e)
5334         , _time_selection_at_start (!_editor->get_selection().time.empty())
5335 {
5336         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5337
5338         if (_time_selection_at_start) {
5339                 start_at_start = _editor->get_selection().time.start();
5340                 end_at_start = _editor->get_selection().time.end_frame();
5341         }
5342 }
5343
5344 void
5345 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5346 {
5347         if (_editor->session() == 0) {
5348                 return;
5349         }
5350
5351         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5352
5353         switch (_operation) {
5354         case CreateSelection:
5355                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5356                         _add = true;
5357                 } else {
5358                         _add = false;
5359                 }
5360                 cursor = _editor->cursors()->selector;
5361                 Drag::start_grab (event, cursor);
5362                 break;
5363
5364         case SelectionStartTrim:
5365                 if (_editor->clicked_axisview) {
5366                         _editor->clicked_axisview->order_selection_trims (_item, true);
5367                 }
5368                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5369                 break;
5370
5371         case SelectionEndTrim:
5372                 if (_editor->clicked_axisview) {
5373                         _editor->clicked_axisview->order_selection_trims (_item, false);
5374                 }
5375                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5376                 break;
5377
5378         case SelectionMove:
5379                 Drag::start_grab (event, cursor);
5380                 break;
5381
5382         case SelectionExtend:
5383                 Drag::start_grab (event, cursor);
5384                 break;
5385         }
5386
5387         if (_operation == SelectionMove) {
5388                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5389         } else {
5390                 show_verbose_cursor_time (adjusted_current_frame (event));
5391         }
5392 }
5393
5394 void
5395 SelectionDrag::setup_pointer_frame_offset ()
5396 {
5397         switch (_operation) {
5398         case CreateSelection:
5399                 _pointer_frame_offset = 0;
5400                 break;
5401
5402         case SelectionStartTrim:
5403         case SelectionMove:
5404                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5405                 break;
5406
5407         case SelectionEndTrim:
5408                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5409                 break;
5410
5411         case SelectionExtend:
5412                 break;
5413         }
5414 }
5415
5416 void
5417 SelectionDrag::motion (GdkEvent* event, bool first_move)
5418 {
5419         framepos_t start = 0;
5420         framepos_t end = 0;
5421         framecnt_t length = 0;
5422         framecnt_t distance = 0;
5423         MusicFrame start_mf (0, 0);
5424         framepos_t const pending_position = adjusted_current_frame (event);
5425
5426         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5427                 return;
5428         }
5429
5430         if (first_move) {
5431                 _track_selection_at_start = _editor->selection->tracks;
5432         }
5433
5434         switch (_operation) {
5435         case CreateSelection:
5436         {
5437                 MusicFrame grab (grab_frame (), 0);
5438                 if (first_move) {
5439                         grab.frame = adjusted_current_frame (event, false);
5440                         if (grab.frame < pending_position) {
5441                                 _editor->snap_to (grab, RoundDownMaybe);
5442                         }  else {
5443                                 _editor->snap_to (grab, RoundUpMaybe);
5444                         }
5445                 }
5446
5447                 if (pending_position < grab.frame) {
5448                         start = pending_position;
5449                         end = grab.frame;
5450                 } else {
5451                         end = pending_position;
5452                         start = grab.frame;
5453                 }
5454
5455                 /* first drag: Either add to the selection
5456                    or create a new selection
5457                 */
5458
5459                 if (first_move) {
5460
5461                         if (_add) {
5462
5463                                 /* adding to the selection */
5464                                 _editor->set_selected_track_as_side_effect (Selection::Add);
5465                                 _editor->clicked_selection = _editor->selection->add (start, end);
5466                                 _add = false;
5467
5468                         } else {
5469
5470                                 /* new selection */
5471
5472                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5473                                         _editor->set_selected_track_as_side_effect (Selection::Set);
5474                                 }
5475
5476                                 _editor->clicked_selection = _editor->selection->set (start, end);
5477                         }
5478                 }
5479
5480                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5481                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5482                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5483                 if (atest) {
5484                         _editor->selection->add (atest);
5485                         break;
5486                 }
5487
5488                 /* select all tracks within the rectangle that we've marked out so far */
5489                 TrackViewList new_selection;
5490                 TrackViewList& all_tracks (_editor->track_views);
5491
5492                 ArdourCanvas::Coord const top = grab_y();
5493                 ArdourCanvas::Coord const bottom = current_pointer_y();
5494
5495                 if (top >= 0 && bottom >= 0) {
5496
5497                         //first, find the tracks that are covered in the y range selection
5498                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5499                                 if ((*i)->covered_by_y_range (top, bottom)) {
5500                                         new_selection.push_back (*i);
5501                                 }
5502                         }
5503
5504                         //now compare our list with the current selection, and add as necessary
5505                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5506                         TrackViewList tracks_to_add;
5507                         TrackViewList tracks_to_remove;
5508                         vector<RouteGroup*> selected_route_groups;
5509
5510                         if (!first_move) {
5511                                 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5512                                         if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5513                                                 tracks_to_remove.push_back (*i);
5514                                         } else {
5515                                                 RouteGroup* rg = (*i)->route_group();
5516                                                 if (rg && rg->is_active() && rg->is_select()) {
5517                                                         selected_route_groups.push_back (rg);
5518                                                 }
5519                                         }
5520                                 }
5521                         }
5522
5523                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5524                                 if (!_editor->selection->tracks.contains (*i)) {
5525                                         tracks_to_add.push_back (*i);
5526                                         RouteGroup* rg = (*i)->route_group();
5527
5528                                         if (rg && rg->is_active() && rg->is_select()) {
5529                                                 selected_route_groups.push_back (rg);
5530                                         }
5531                                 }
5532                         }
5533
5534                         _editor->selection->add (tracks_to_add);
5535
5536                         if (!tracks_to_remove.empty()) {
5537
5538                                 /* check all these to-be-removed tracks against the
5539                                  * possibility that they are selected by being
5540                                  * in the same group as an approved track.
5541                                  */
5542
5543                                 for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
5544                                         RouteGroup* rg = (*i)->route_group();
5545
5546                                         if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
5547                                                 i = tracks_to_remove.erase (i);
5548                                         } else {
5549                                                 ++i;
5550                                         }
5551                                 }
5552
5553                                 /* remove whatever is left */
5554
5555                                 _editor->selection->remove (tracks_to_remove);
5556                         }
5557                 }
5558         }
5559         break;
5560
5561         case SelectionStartTrim:
5562
5563                 end = _editor->selection->time[_editor->clicked_selection].end;
5564
5565                 if (pending_position > end) {
5566                         start = end;
5567                 } else {
5568                         start = pending_position;
5569                 }
5570                 break;
5571
5572         case SelectionEndTrim:
5573
5574                 start = _editor->selection->time[_editor->clicked_selection].start;
5575
5576                 if (pending_position < start) {
5577                         end = start;
5578                 } else {
5579                         end = pending_position;
5580                 }
5581
5582                 break;
5583
5584         case SelectionMove:
5585
5586                 start = _editor->selection->time[_editor->clicked_selection].start;
5587                 end = _editor->selection->time[_editor->clicked_selection].end;
5588
5589                 length = end - start;
5590                 distance = pending_position - start;
5591                 start = pending_position;
5592
5593                 start_mf.frame = start;
5594                 _editor->snap_to (start_mf);
5595
5596                 end = start_mf.frame + length;
5597
5598                 break;
5599
5600         case SelectionExtend:
5601                 break;
5602         }
5603
5604         if (start != end) {
5605                 switch (_operation) {
5606                 case SelectionMove:
5607                         if (_time_selection_at_start) {
5608                                 _editor->selection->move_time (distance);
5609                         }
5610                         break;
5611                 default:
5612                         _editor->selection->replace (_editor->clicked_selection, start, end);
5613                 }
5614         }
5615
5616         if (_operation == SelectionMove) {
5617                 show_verbose_cursor_time(start);
5618         } else {
5619                 show_verbose_cursor_time(pending_position);
5620         }
5621 }
5622
5623 void
5624 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5625 {
5626         Session* s = _editor->session();
5627
5628         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5629         if (movement_occurred) {
5630                 motion (event, false);
5631                 /* XXX this is not object-oriented programming at all. ick */
5632                 if (_editor->selection->time.consolidate()) {
5633                         _editor->selection->TimeChanged ();
5634                 }
5635
5636                 /* XXX what if its a music time selection? */
5637                 if (s) {
5638                         if (s->get_play_range() && s->transport_rolling()) {
5639                                 s->request_play_range (&_editor->selection->time, true);
5640                         } else if (!s->config.get_external_sync()) {
5641                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5642                                         s->request_locate (_editor->get_selection().time.start());
5643                                 }
5644                         }
5645
5646                         if (_editor->get_selection().time.length() != 0) {
5647                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5648                         } else {
5649                                 s->clear_range_selection ();
5650                         }
5651                 }
5652
5653         } else {
5654                 /* just a click, no pointer movement.
5655                  */
5656
5657                 if (was_double_click()) {
5658                         if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5659                                 _editor->temporal_zoom_selection (Both);
5660                                 return;
5661                         }
5662                 }
5663
5664                 if (_operation == SelectionExtend) {
5665                         if (_time_selection_at_start) {
5666                                 framepos_t pos = adjusted_current_frame (event, false);
5667                                 framepos_t start = min (pos, start_at_start);
5668                                 framepos_t end = max (pos, end_at_start);
5669                                 _editor->selection->set (start, end);
5670                         }
5671                 } else {
5672                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5673                                 if (_editor->clicked_selection) {
5674                                         _editor->selection->remove (_editor->clicked_selection);
5675                                 }
5676                         } else {
5677                                 if (!_editor->clicked_selection) {
5678                                         _editor->selection->clear_time();
5679                                 }
5680                         }
5681                 }
5682
5683                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5684                         _editor->selection->set (_editor->clicked_axisview);
5685                 }
5686
5687                 if (s && s->get_play_range () && s->transport_rolling()) {
5688                         s->request_stop (false, false);
5689                 }
5690
5691         }
5692
5693         _editor->stop_canvas_autoscroll ();
5694         _editor->clicked_selection = 0;
5695         _editor->commit_reversible_selection_op ();
5696 }
5697
5698 void
5699 SelectionDrag::aborted (bool)
5700 {
5701         /* XXX: TODO */
5702 }
5703
5704 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5705         : Drag (e, i, false),
5706           _operation (o),
5707           _copy (false)
5708 {
5709         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5710
5711         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5712                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5713                                                                       physical_screen_height (_editor->current_toplevel()->get_window())));
5714         _drag_rect->hide ();
5715
5716         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5717         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5718 }
5719
5720 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5721 {
5722         /* normal canvas items will be cleaned up when their parent group is deleted. But
5723            this item is created as the child of a long-lived parent group, and so we
5724            need to explicitly delete it.
5725         */
5726         delete _drag_rect;
5727 }
5728
5729 void
5730 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5731 {
5732         if (_editor->session() == 0) {
5733                 return;
5734         }
5735
5736         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5737
5738         if (!_editor->temp_location) {
5739                 _editor->temp_location = new Location (*_editor->session());
5740         }
5741
5742         switch (_operation) {
5743         case CreateSkipMarker:
5744         case CreateRangeMarker:
5745         case CreateTransportMarker:
5746         case CreateCDMarker:
5747
5748                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5749                         _copy = true;
5750                 } else {
5751                         _copy = false;
5752                 }
5753                 cursor = _editor->cursors()->selector;
5754                 break;
5755         }
5756
5757         Drag::start_grab (event, cursor);
5758
5759         show_verbose_cursor_time (adjusted_current_frame (event));
5760 }
5761
5762 void
5763 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5764 {
5765         framepos_t start = 0;
5766         framepos_t end = 0;
5767         ArdourCanvas::Rectangle *crect;
5768
5769         switch (_operation) {
5770         case CreateSkipMarker:
5771                 crect = _editor->range_bar_drag_rect;
5772                 break;
5773         case CreateRangeMarker:
5774                 crect = _editor->range_bar_drag_rect;
5775                 break;
5776         case CreateTransportMarker:
5777                 crect = _editor->transport_bar_drag_rect;
5778                 break;
5779         case CreateCDMarker:
5780                 crect = _editor->cd_marker_bar_drag_rect;
5781                 break;
5782         default:
5783                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5784                 return;
5785                 break;
5786         }
5787
5788         framepos_t const pf = adjusted_current_frame (event);
5789
5790         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5791                 MusicFrame grab (grab_frame (), 0);
5792                 _editor->snap_to (grab);
5793
5794                 if (pf < grab_frame()) {
5795                         start = pf;
5796                         end = grab.frame;
5797                 } else {
5798                         end = pf;
5799                         start = grab.frame;
5800                 }
5801
5802                 /* first drag: Either add to the selection
5803                    or create a new selection.
5804                 */
5805
5806                 if (first_move) {
5807
5808                         _editor->temp_location->set (start, end);
5809
5810                         crect->show ();
5811
5812                         update_item (_editor->temp_location);
5813                         _drag_rect->show();
5814                         //_drag_rect->raise_to_top();
5815
5816                 }
5817         }
5818
5819         if (start != end) {
5820                 _editor->temp_location->set (start, end);
5821
5822                 double x1 = _editor->sample_to_pixel (start);
5823                 double x2 = _editor->sample_to_pixel (end);
5824                 crect->set_x0 (x1);
5825                 crect->set_x1 (x2);
5826
5827                 update_item (_editor->temp_location);
5828         }
5829
5830         show_verbose_cursor_time (pf);
5831
5832 }
5833
5834 void
5835 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5836 {
5837         Location * newloc = 0;
5838         string rangename;
5839         int flags;
5840
5841         if (movement_occurred) {
5842                 motion (event, false);
5843                 _drag_rect->hide();
5844
5845                 switch (_operation) {
5846                 case CreateSkipMarker:
5847                 case CreateRangeMarker:
5848                 case CreateCDMarker:
5849                     {
5850                         XMLNode &before = _editor->session()->locations()->get_state();
5851                         if (_operation == CreateSkipMarker) {
5852                                 _editor->begin_reversible_command (_("new skip marker"));
5853                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5854                                 flags = Location::IsRangeMarker | Location::IsSkip;
5855                                 _editor->range_bar_drag_rect->hide();
5856                         } else if (_operation == CreateCDMarker) {
5857                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5858                                 _editor->begin_reversible_command (_("new CD marker"));
5859                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5860                                 _editor->cd_marker_bar_drag_rect->hide();
5861                         } else {
5862                                 _editor->begin_reversible_command (_("new skip marker"));
5863                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5864                                 flags = Location::IsRangeMarker;
5865                                 _editor->range_bar_drag_rect->hide();
5866                         }
5867                         newloc = new Location (
5868                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5869                                 , _editor->get_grid_music_divisions (event->button.state));
5870
5871                         _editor->session()->locations()->add (newloc, true);
5872                         XMLNode &after = _editor->session()->locations()->get_state();
5873                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5874                         _editor->commit_reversible_command ();
5875                         break;
5876                     }
5877
5878                 case CreateTransportMarker:
5879                         // popup menu to pick loop or punch
5880                         _editor->new_transport_marker_context_menu (&event->button, _item);
5881                         break;
5882                 }
5883
5884         } else {
5885
5886                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5887
5888                 if (_operation == CreateTransportMarker) {
5889
5890                         /* didn't drag, so just locate */
5891
5892                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5893
5894                 } else if (_operation == CreateCDMarker) {
5895
5896                         /* didn't drag, but mark is already created so do
5897                          * nothing */
5898
5899                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5900
5901                         framepos_t start;
5902                         framepos_t end;
5903
5904                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5905
5906                         if (end == max_framepos) {
5907                                 end = _editor->session()->current_end_frame ();
5908                         }
5909
5910                         if (start == max_framepos) {
5911                                 start = _editor->session()->current_start_frame ();
5912                         }
5913
5914                         switch (_editor->mouse_mode) {
5915                         case MouseObject:
5916                                 /* find the two markers on either side and then make the selection from it */
5917                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5918                                 break;
5919
5920                         case MouseRange:
5921                                 /* find the two markers on either side of the click and make the range out of it */
5922                                 _editor->selection->set (start, end);
5923                                 break;
5924
5925                         default:
5926                                 break;
5927                         }
5928                 }
5929         }
5930
5931         _editor->stop_canvas_autoscroll ();
5932 }
5933
5934 void
5935 RangeMarkerBarDrag::aborted (bool movement_occurred)
5936 {
5937         if (movement_occurred) {
5938                 _drag_rect->hide ();
5939         }
5940 }
5941
5942 void
5943 RangeMarkerBarDrag::update_item (Location* location)
5944 {
5945         double const x1 = _editor->sample_to_pixel (location->start());
5946         double const x2 = _editor->sample_to_pixel (location->end());
5947
5948         _drag_rect->set_x0 (x1);
5949         _drag_rect->set_x1 (x2);
5950 }
5951
5952 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5953         : Drag (e, i)
5954         , _cumulative_dx (0)
5955         , _cumulative_dy (0)
5956         , _earliest (0.0)
5957         , _was_selected (false)
5958         , _copy (false)
5959 {
5960         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5961
5962         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5963         assert (_primary);
5964         _region = &_primary->region_view ();
5965         _note_height = _region->midi_stream_view()->note_height ();
5966 }
5967
5968 void
5969 NoteDrag::setup_pointer_frame_offset ()
5970 {
5971         _pointer_frame_offset = raw_grab_frame()
5972                 - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
5973 }
5974
5975 void
5976 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5977 {
5978         Drag::start_grab (event);
5979
5980         if (ArdourKeyboard::indicates_copy (event->button.state)) {
5981                 _copy = true;
5982         } else {
5983                 _copy = false;
5984         }
5985
5986         setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
5987
5988         if (!(_was_selected = _primary->selected())) {
5989
5990                 /* tertiary-click means extend selection - we'll do that on button release,
5991                    so don't add it here, because otherwise we make it hard to figure
5992                    out the "extend-to" range.
5993                 */
5994
5995                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5996
5997                 if (!extend) {
5998                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5999
6000                         if (add) {
6001                                 _region->note_selected (_primary, true);
6002                         } else {
6003                                 _editor->get_selection().clear_points();
6004                                 _region->unique_select (_primary);
6005                         }
6006                 }
6007         }
6008 }
6009
6010 /** @return Current total drag x change in quarter notes */
6011 double
6012 NoteDrag::total_dx (GdkEvent * event) const
6013 {
6014         if (_x_constrained) {
6015                 return 0;
6016         }
6017
6018         TempoMap& map (_editor->session()->tempo_map());
6019
6020         /* dx in frames */
6021         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
6022
6023         /* primary note time */
6024         frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
6025
6026         /* primary note time in quarter notes */
6027         double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
6028
6029         /* new time of the primary note in session frames */
6030         frameoffset_t st = n + dx + snap_delta (event->button.state);
6031
6032         /* possibly snap and return corresponding delta in quarter notes */
6033         MusicFrame snap (st, 0);
6034         _editor->snap_to_with_modifier (snap, event);
6035         double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
6036
6037         /* prevent the earliest note being dragged earlier than the region's start position */
6038         if (_earliest + ret < _region->midi_region()->start_beats()) {
6039                 ret -= (_earliest + ret) - _region->midi_region()->start_beats();
6040         }
6041
6042         return ret;
6043 }
6044
6045 /** @return Current total drag y change in note number */
6046 int8_t
6047 NoteDrag::total_dy () const
6048 {
6049         if (_y_constrained) {
6050                 return 0;
6051         }
6052
6053         double const y = _region->midi_view()->y_position ();
6054         /* new current note */
6055         uint8_t n = _region->y_to_note (current_pointer_y () - y);
6056         /* clamp */
6057         MidiStreamView* msv = _region->midi_stream_view ();
6058         n = max (msv->lowest_note(), n);
6059         n = min (msv->highest_note(), n);
6060         /* and work out delta */
6061         return n - _region->y_to_note (grab_y() - y);
6062 }
6063
6064 void
6065 NoteDrag::motion (GdkEvent * event, bool first_move)
6066 {
6067         if (first_move) {
6068                 _earliest = _region->earliest_in_selection().to_double();
6069                 if (_copy) {
6070                         /* make copies of all the selected notes */
6071                         _primary = _region->copy_selection (_primary);
6072                 }
6073         }
6074
6075         /* Total change in x and y since the start of the drag */
6076         double const dx_qn = total_dx (event);
6077         int8_t const dy = total_dy ();
6078
6079         /* Now work out what we have to do to the note canvas items to set this new drag delta */
6080         double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
6081         double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
6082
6083         if (tdx || tdy) {
6084                 _cumulative_dx = dx_qn;
6085                 _cumulative_dy += tdy;
6086
6087                 int8_t note_delta = total_dy();
6088
6089                 if (tdx || tdy) {
6090                         if (_copy) {
6091                                 _region->move_copies (dx_qn, tdy, note_delta);
6092                         } else {
6093                                 _region->move_selection (dx_qn, tdy, note_delta);
6094                         }
6095
6096                         /* the new note value may be the same as the old one, but we
6097                          * don't know what that means because the selection may have
6098                          * involved more than one note and we might be doing something
6099                          * odd with them. so show the note value anyway, always.
6100                          */
6101
6102                         uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
6103
6104                         _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
6105                 }
6106         }
6107 }
6108
6109 void
6110 NoteDrag::finished (GdkEvent* ev, bool moved)
6111 {
6112         if (!moved) {
6113                 /* no motion - select note */
6114
6115                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
6116                     _editor->current_mouse_mode() == Editing::MouseDraw) {
6117
6118                         bool changed = false;
6119
6120                         if (_was_selected) {
6121                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6122                                 if (add) {
6123                                         _region->note_deselected (_primary);
6124                                         changed = true;
6125                                 } else {
6126                                         _editor->get_selection().clear_points();
6127                                         _region->unique_select (_primary);
6128                                         changed = true;
6129                                 }
6130                         } else {
6131                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
6132                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6133
6134                                 if (!extend && !add && _region->selection_size() > 1) {
6135                                         _editor->get_selection().clear_points();
6136                                         _region->unique_select (_primary);
6137                                         changed = true;
6138                                 } else if (extend) {
6139                                         _region->note_selected (_primary, true, true);
6140                                         changed = true;
6141                                 } else {
6142                                         /* it was added during button press */
6143                                         changed = true;
6144
6145                                 }
6146                         }
6147
6148                         if (changed) {
6149                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
6150                                 _editor->commit_reversible_selection_op();
6151                         }
6152                 }
6153         } else {
6154                 _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
6155         }
6156 }
6157
6158 void
6159 NoteDrag::aborted (bool)
6160 {
6161         /* XXX: TODO */
6162 }
6163
6164 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
6165 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
6166         : Drag (editor, atv->base_item ())
6167         , _ranges (r)
6168         , _y_origin (atv->y_position())
6169         , _nothing_to_drag (false)
6170 {
6171         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6172         setup (atv->lines ());
6173 }
6174
6175 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
6176 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
6177         : Drag (editor, rv->get_canvas_group ())
6178         , _ranges (r)
6179         , _y_origin (rv->get_time_axis_view().y_position())
6180         , _nothing_to_drag (false)
6181         , _integral (false)
6182 {
6183         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6184
6185         list<boost::shared_ptr<AutomationLine> > lines;
6186
6187         AudioRegionView*      audio_view;
6188         AutomationRegionView* automation_view;
6189         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
6190                 lines.push_back (audio_view->get_gain_line ());
6191         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
6192                 lines.push_back (automation_view->line ());
6193                 _integral = true;
6194         } else {
6195                 error << _("Automation range drag created for invalid region type") << endmsg;
6196         }
6197
6198         setup (lines);
6199 }
6200
6201 /** @param lines AutomationLines to drag.
6202  *  @param offset Offset from the session start to the points in the AutomationLines.
6203  */
6204 void
6205 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
6206 {
6207         /* find the lines that overlap the ranges being dragged */
6208         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
6209         while (i != lines.end ()) {
6210                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
6211                 ++j;
6212
6213                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
6214
6215                 /* check this range against all the AudioRanges that we are using */
6216                 list<AudioRange>::const_iterator k = _ranges.begin ();
6217                 while (k != _ranges.end()) {
6218                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
6219                                 break;
6220                         }
6221                         ++k;
6222                 }
6223
6224                 /* add it to our list if it overlaps at all */
6225                 if (k != _ranges.end()) {
6226                         Line n;
6227                         n.line = *i;
6228                         n.state = 0;
6229                         n.range = r;
6230                         _lines.push_back (n);
6231                 }
6232
6233                 i = j;
6234         }
6235
6236         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
6237 }
6238
6239 double
6240 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
6241 {
6242         return 1.0 - ((global_y - _y_origin) / line->height());
6243 }
6244
6245 double
6246 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
6247 {
6248         const double v = list->eval(x);
6249         return _integral ? rint(v) : v;
6250 }
6251
6252 void
6253 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6254 {
6255         Drag::start_grab (event, cursor);
6256
6257         /* Get line states before we start changing things */
6258         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6259                 i->state = &i->line->get_state ();
6260                 i->original_fraction = y_fraction (i->line, current_pointer_y());
6261         }
6262
6263         if (_ranges.empty()) {
6264
6265                 /* No selected time ranges: drag all points */
6266                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6267                         uint32_t const N = i->line->npoints ();
6268                         for (uint32_t j = 0; j < N; ++j) {
6269                                 i->points.push_back (i->line->nth (j));
6270                         }
6271                 }
6272
6273         }
6274
6275         if (_nothing_to_drag) {
6276                 return;
6277         }
6278 }
6279
6280 void
6281 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
6282 {
6283         if (_nothing_to_drag && !first_move) {
6284                 return;
6285         }
6286
6287         if (first_move) {
6288                 _editor->begin_reversible_command (_("automation range move"));
6289
6290                 if (!_ranges.empty()) {
6291
6292                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
6293
6294                                 framecnt_t const half = (i->start + i->end) / 2;
6295
6296                                 /* find the line that this audio range starts in */
6297                                 list<Line>::iterator j = _lines.begin();
6298                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
6299                                         ++j;
6300                                 }
6301
6302                                 if (j != _lines.end()) {
6303                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6304
6305                                 /* j is the line that this audio range starts in; fade into it;
6306                                    64 samples length plucked out of thin air.
6307                                 */
6308
6309                                         framepos_t a = i->start + 64;
6310                                         if (a > half) {
6311                                                 a = half;
6312                                         }
6313
6314                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
6315                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
6316
6317                                         XMLNode &before = the_list->get_state();
6318                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6319                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6320
6321                                         if (add_p || add_q) {
6322                                                 _editor->session()->add_command (
6323                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6324                                         }
6325                                 }
6326
6327                                 /* same thing for the end */
6328
6329                                 j = _lines.begin();
6330                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6331                                         ++j;
6332                                 }
6333
6334                                 if (j != _lines.end()) {
6335                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6336
6337                                         /* j is the line that this audio range starts in; fade out of it;
6338                                            64 samples length plucked out of thin air.
6339                                         */
6340
6341                                         framepos_t b = i->end - 64;
6342                                         if (b < half) {
6343                                                 b = half;
6344                                         }
6345
6346                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6347                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6348
6349                                         XMLNode &before = the_list->get_state();
6350                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6351                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6352
6353                                         if (add_p || add_q) {
6354                                                 _editor->session()->add_command (
6355                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6356                                         }
6357                                 }
6358                         }
6359
6360                         _nothing_to_drag = true;
6361
6362                         /* Find all the points that should be dragged and put them in the relevant
6363                            points lists in the Line structs.
6364                         */
6365
6366                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6367
6368                                 uint32_t const N = i->line->npoints ();
6369                                 for (uint32_t j = 0; j < N; ++j) {
6370
6371                                         /* here's a control point on this line */
6372                                         ControlPoint* p = i->line->nth (j);
6373                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6374
6375                                         /* see if it's inside a range */
6376                                         list<AudioRange>::const_iterator k = _ranges.begin ();
6377                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6378                                                 ++k;
6379                                         }
6380
6381                                         if (k != _ranges.end()) {
6382                                                 /* dragging this point */
6383                                                 _nothing_to_drag = false;
6384                                                 i->points.push_back (p);
6385                                         }
6386                                 }
6387                         }
6388                 }
6389
6390                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6391                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6392                 }
6393         }
6394
6395         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6396                 float const f = y_fraction (l->line, current_pointer_y());
6397                 /* we are ignoring x position for this drag, so we can just pass in anything */
6398                 pair<float, float> result;
6399                 uint32_t ignored;
6400                 result = l->line->drag_motion (0, f, true, false, ignored);
6401                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (result.first, result.second));
6402         }
6403 }
6404
6405 void
6406 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6407 {
6408         if (_nothing_to_drag || !motion_occurred) {
6409                 return;
6410         }
6411
6412         motion (event, false);
6413         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6414                 i->line->end_drag (false, 0);
6415         }
6416
6417         _editor->commit_reversible_command ();
6418 }
6419
6420 void
6421 AutomationRangeDrag::aborted (bool)
6422 {
6423         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6424                 i->line->reset ();
6425         }
6426 }
6427
6428 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6429         : view (v)
6430         , initial_time_axis_view (itav)
6431 {
6432         /* note that time_axis_view may be null if the regionview was created
6433          * as part of a copy operation.
6434          */
6435         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6436         layer = v->region()->layer ();
6437         initial_y = v->get_canvas_group()->position().y;
6438         initial_playlist = v->region()->playlist ();
6439         initial_position = v->region()->position ();
6440         initial_end = v->region()->position () + v->region()->length ();
6441 }
6442
6443 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6444         : Drag (e, i->canvas_item ())
6445         , _region_view (r)
6446         , _patch_change (i)
6447         , _cumulative_dx (0)
6448 {
6449         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6450                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6451                                                    grab_frame()));
6452 }
6453
6454 void
6455 PatchChangeDrag::motion (GdkEvent* ev, bool)
6456 {
6457         framepos_t f = adjusted_current_frame (ev);
6458         boost::shared_ptr<Region> r = _region_view->region ();
6459         f = max (f, r->position ());
6460         f = min (f, r->last_frame ());
6461
6462         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6463         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6464         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6465         _cumulative_dx = dxu;
6466 }
6467
6468 void
6469 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6470 {
6471         if (!movement_occurred) {
6472                 if (was_double_click()) {
6473                         _region_view->edit_patch_change (_patch_change);
6474                 }
6475                 return;
6476         }
6477
6478         boost::shared_ptr<Region> r (_region_view->region ());
6479         framepos_t f = adjusted_current_frame (ev);
6480         f = max (f, r->position ());
6481         f = min (f, r->last_frame ());
6482
6483         _region_view->move_patch_change (
6484                 *_patch_change,
6485                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6486                 );
6487 }
6488
6489 void
6490 PatchChangeDrag::aborted (bool)
6491 {
6492         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6493 }
6494
6495 void
6496 PatchChangeDrag::setup_pointer_frame_offset ()
6497 {
6498         boost::shared_ptr<Region> region = _region_view->region ();
6499         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6500 }
6501
6502 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6503         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6504         , _region_view (rv)
6505 {
6506
6507 }
6508
6509 void
6510 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6511 {
6512         _region_view->update_drag_selection (
6513                 x1, x2, y1, y2,
6514                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6515 }
6516
6517 void
6518 MidiRubberbandSelectDrag::deselect_things ()
6519 {
6520         /* XXX */
6521 }
6522
6523 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6524         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6525         , _region_view (rv)
6526 {
6527         _vertical_only = true;
6528 }
6529
6530 void
6531 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6532 {
6533         double const y = _region_view->midi_view()->y_position ();
6534
6535         y1 = max (0.0, y1 - y);
6536         y2 = max (0.0, y2 - y);
6537
6538         _region_view->update_vertical_drag_selection (
6539                 y1,
6540                 y2,
6541                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6542                 );
6543 }
6544
6545 void
6546 MidiVerticalSelectDrag::deselect_things ()
6547 {
6548         /* XXX */
6549 }
6550
6551 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6552         : RubberbandSelectDrag (e, i)
6553 {
6554
6555 }
6556
6557 void
6558 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6559 {
6560         if (drag_in_progress) {
6561                 /* We just want to select things at the end of the drag, not during it */
6562                 return;
6563         }
6564
6565         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6566
6567         _editor->begin_reversible_selection_op (X_("rubberband selection"));
6568
6569         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6570
6571         _editor->commit_reversible_selection_op ();
6572 }
6573
6574 void
6575 EditorRubberbandSelectDrag::deselect_things ()
6576 {
6577         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6578
6579         _editor->selection->clear_tracks();
6580         _editor->selection->clear_regions();
6581         _editor->selection->clear_points ();
6582         _editor->selection->clear_lines ();
6583         _editor->selection->clear_midi_notes ();
6584
6585         _editor->commit_reversible_selection_op();
6586 }
6587
6588 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6589         : Drag (e, i)
6590         , _region_view (rv)
6591         , _drag_rect (0)
6592 {
6593         _note[0] = _note[1] = 0;
6594 }
6595
6596 NoteCreateDrag::~NoteCreateDrag ()
6597 {
6598         delete _drag_rect;
6599 }
6600
6601 framecnt_t
6602 NoteCreateDrag::grid_frames (framepos_t t) const
6603 {
6604
6605         const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6606         const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6607
6608         return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6609                 - _region_view->region_beats_to_region_frames (t_beats);
6610 }
6611
6612 void
6613 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6614 {
6615         Drag::start_grab (event, cursor);
6616
6617         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6618         TempoMap& map (_editor->session()->tempo_map());
6619
6620         const framepos_t pf = _drags->current_pointer_frame ();
6621         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6622
6623         const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6624
6625         double eqaf = map.exact_qn_at_frame (pf, divisions);
6626
6627         if (divisions != 0) {
6628
6629                 const double qaf = map.quarter_note_at_frame (pf);
6630
6631                 /* Hack so that we always snap to the note that we are over, instead of snapping
6632                    to the next one if we're more than halfway through the one we're over.
6633                 */
6634
6635                 const double rem = eqaf - qaf;
6636                 if (rem >= 0.0) {
6637                         eqaf -= grid_beats.to_double();
6638                 }
6639         }
6640
6641         _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6642         /* minimum initial length is grid beats */
6643         _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6644
6645         double const x0 = _editor->sample_to_pixel (_note[0]);
6646         double const x1 = _editor->sample_to_pixel (_note[1]);
6647         double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6648
6649         _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6650         _drag_rect->set_outline_all ();
6651         _drag_rect->set_outline_color (0xffffff99);
6652         _drag_rect->set_fill_color (0xffffff66);
6653 }
6654
6655 void
6656 NoteCreateDrag::motion (GdkEvent* event, bool)
6657 {
6658         TempoMap& map (_editor->session()->tempo_map());
6659         const framepos_t pf = _drags->current_pointer_frame ();
6660         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6661         double eqaf = map.exact_qn_at_frame (pf, divisions);
6662
6663         if (divisions != 0) {
6664
6665                 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6666
6667                 const double qaf = map.quarter_note_at_frame (pf);
6668                 /* Hack so that we always snap to the note that we are over, instead of snapping
6669                    to the next one if we're more than halfway through the one we're over.
6670                 */
6671
6672                 const double rem = eqaf - qaf;
6673                 if (rem >= 0.0) {
6674                         eqaf -= grid_beats.to_double();
6675                 }
6676
6677                 eqaf += grid_beats.to_double();
6678         }
6679         _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6680
6681         double const x0 = _editor->sample_to_pixel (_note[0]);
6682         double const x1 = _editor->sample_to_pixel (_note[1]);
6683         _drag_rect->set_x0 (std::min(x0, x1));
6684         _drag_rect->set_x1 (std::max(x0, x1));
6685 }
6686
6687 void
6688 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6689 {
6690         /* we create a note even if there was no movement */
6691         framepos_t const start = min (_note[0], _note[1]);
6692         framepos_t const start_sess_rel = start + _region_view->region()->position();
6693         framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6694         framecnt_t const g = grid_frames (start_sess_rel);
6695
6696         if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6697                 length = g;
6698         }
6699
6700         TempoMap& map (_editor->session()->tempo_map());
6701         const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6702         Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6703
6704         _editor->begin_reversible_command (_("Create Note"));
6705         _region_view->clear_editor_note_selection();
6706         _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6707         _editor->commit_reversible_command ();
6708 }
6709
6710 double
6711 NoteCreateDrag::y_to_region (double y) const
6712 {
6713         double x = 0;
6714         _region_view->get_canvas_group()->canvas_to_item (x, y);
6715         return y;
6716 }
6717
6718 void
6719 NoteCreateDrag::aborted (bool)
6720 {
6721
6722 }
6723
6724 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6725         : Drag (e, i)
6726         , _region_view (rv)
6727         , _last_pos (0)
6728         , _y (0.0)
6729 {
6730 }
6731
6732 HitCreateDrag::~HitCreateDrag ()
6733 {
6734 }
6735
6736 void
6737 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6738 {
6739         Drag::start_grab (event, cursor);
6740
6741         TempoMap& map (_editor->session()->tempo_map());
6742
6743         _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6744
6745         const framepos_t pf = _drags->current_pointer_frame ();
6746         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6747
6748         const double eqaf = map.exact_qn_at_frame (pf, divisions);
6749
6750         boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6751
6752         if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6753                 return;
6754         }
6755
6756         const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6757         Evoral::Beats length = _region_view->get_grid_beats (pf);
6758
6759         _editor->begin_reversible_command (_("Create Hit"));
6760         _region_view->clear_editor_note_selection();
6761         _region_view->create_note_at (start, _y, length, event->button.state, false);
6762
6763         _last_pos = start;
6764 }
6765
6766 void
6767 HitCreateDrag::motion (GdkEvent* event, bool)
6768 {
6769         TempoMap& map (_editor->session()->tempo_map());
6770
6771         const framepos_t pf = _drags->current_pointer_frame ();
6772         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6773
6774         if (divisions == 0) {
6775                 return;
6776         }
6777
6778         const double eqaf = map.exact_qn_at_frame (pf, divisions);
6779         const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6780
6781         if (_last_pos == start) {
6782                 return;
6783         }
6784
6785         Evoral::Beats length = _region_view->get_grid_beats (pf);
6786
6787         boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6788
6789         if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6790                 return;
6791         }
6792
6793         _region_view->create_note_at (start, _y, length, event->button.state, false);
6794
6795         _last_pos = start;
6796 }
6797
6798 void
6799 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6800 {
6801         _editor->commit_reversible_command ();
6802
6803 }
6804
6805 double
6806 HitCreateDrag::y_to_region (double y) const
6807 {
6808         double x = 0;
6809         _region_view->get_canvas_group()->canvas_to_item (x, y);
6810         return y;
6811 }
6812
6813 void
6814 HitCreateDrag::aborted (bool)
6815 {
6816         // umm..
6817 }
6818
6819 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6820         : Drag (e, i)
6821         , arv (rv)
6822         , start (start_yn)
6823 {
6824         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
6825 }
6826
6827 void
6828 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6829 {
6830         Drag::start_grab (event, cursor);
6831 }
6832
6833 void
6834 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6835 {
6836         double distance;
6837         double new_length;
6838         framecnt_t len;
6839
6840         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6841
6842         if (start) {
6843                 distance = _drags->current_pointer_x() - grab_x();
6844                 len = ar->fade_in()->back()->when;
6845         } else {
6846                 distance = grab_x() - _drags->current_pointer_x();
6847                 len = ar->fade_out()->back()->when;
6848         }
6849
6850         /* how long should it be ? */
6851
6852         new_length = len + _editor->pixel_to_sample (distance);
6853
6854         /* now check with the region that this is legal */
6855
6856         new_length = ar->verify_xfade_bounds (new_length, start);
6857
6858         if (start) {
6859                 arv->reset_fade_in_shape_width (ar, new_length);
6860         } else {
6861                 arv->reset_fade_out_shape_width (ar, new_length);
6862         }
6863 }
6864
6865 void
6866 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6867 {
6868         double distance;
6869         double new_length;
6870         framecnt_t len;
6871
6872         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6873
6874         if (start) {
6875                 distance = _drags->current_pointer_x() - grab_x();
6876                 len = ar->fade_in()->back()->when;
6877         } else {
6878                 distance = grab_x() - _drags->current_pointer_x();
6879                 len = ar->fade_out()->back()->when;
6880         }
6881
6882         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6883
6884         _editor->begin_reversible_command ("xfade trim");
6885         ar->playlist()->clear_owned_changes ();
6886
6887         if (start) {
6888                 ar->set_fade_in_length (new_length);
6889         } else {
6890                 ar->set_fade_out_length (new_length);
6891         }
6892
6893         /* Adjusting the xfade may affect other regions in the playlist, so we need
6894            to get undo Commands from the whole playlist rather than just the
6895            region.
6896         */
6897
6898         vector<Command*> cmds;
6899         ar->playlist()->rdiff (cmds);
6900         _editor->session()->add_commands (cmds);
6901         _editor->commit_reversible_command ();
6902
6903 }
6904
6905 void
6906 CrossfadeEdgeDrag::aborted (bool)
6907 {
6908         if (start) {
6909                 // arv->redraw_start_xfade ();
6910         } else {
6911                 // arv->redraw_end_xfade ();
6912         }
6913 }
6914
6915 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6916         : Drag (e, item, true)
6917         , line (new EditorCursor (*e))
6918 {
6919         line->set_position (pos);
6920         line->show ();
6921         line->track_canvas_item().reparent (_editor->_drag_motion_group);
6922 }
6923
6924 RegionCutDrag::~RegionCutDrag ()
6925 {
6926         delete line;
6927 }
6928
6929 void
6930 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6931 {
6932         Drag::start_grab (event, c);
6933         motion (event, false);
6934 }
6935
6936 void
6937 RegionCutDrag::motion (GdkEvent* event, bool)
6938 {
6939         MusicFrame pos (_drags->current_pointer_frame(), 0);
6940         _editor->snap_to_with_modifier (pos, event);
6941
6942         line->set_position (pos.frame);
6943 }
6944
6945 void
6946 RegionCutDrag::finished (GdkEvent* event, bool)
6947 {
6948         _editor->get_track_canvas()->canvas()->re_enter();
6949
6950
6951         MusicFrame pos (_drags->current_pointer_frame(), 0);
6952         _editor->snap_to_with_modifier (pos, event);
6953         line->hide ();
6954
6955         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
6956
6957         if (rs.empty()) {
6958                 return;
6959         }
6960
6961         _editor->split_regions_at (pos, rs, false);
6962 }
6963
6964 void
6965 RegionCutDrag::aborted (bool)
6966 {
6967 }
6968
6969 RulerZoomDrag::RulerZoomDrag (Editor* e, ArdourCanvas::Item* item)
6970         : Drag (e, item, true)
6971 {
6972 }
6973
6974 void
6975 RulerZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6976 {
6977         Drag::start_grab (event, c);
6978
6979         framepos_t where = _editor->canvas_event_sample(event);
6980
6981         _editor->_dragging_playhead = true;
6982
6983         _editor->playhead_cursor->set_position (where);
6984 }
6985
6986 void
6987 RulerZoomDrag::motion (GdkEvent* event, bool)
6988 {
6989         framepos_t where = _editor->canvas_event_sample(event);
6990
6991         _editor->playhead_cursor->set_position (where);
6992
6993         const double movement_limit = 20.0;
6994         const double scale = 1.08;
6995         const double y_delta = last_pointer_y() - current_pointer_y();
6996
6997         if (y_delta > 0 && y_delta < movement_limit) {
6998                 _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
6999         } else if (y_delta < 0 && y_delta > -movement_limit) {
7000                 _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
7001         }
7002 }
7003
7004 void
7005 RulerZoomDrag::finished (GdkEvent*, bool)
7006 {
7007         _editor->_dragging_playhead = false;
7008
7009         Session* s = _editor->session ();
7010         if (s) {
7011                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
7012                 _editor->_pending_locate_request = true;
7013         }
7014
7015 }
7016
7017 void
7018 RulerZoomDrag::aborted (bool)
7019 {
7020 }