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