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