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