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