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