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