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