Merge branch 'canvasredesign' into cairocanvas
[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         /* calling this implies that we expect the event to have canvas
181          * coordinates 
182          *
183          * Can we guarantee that this is true?
184          */
185
186         _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
187
188         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
189                 bool const t = (*i)->motion_handler (e, from_autoscroll);
190                 /* run all handlers; return true if at least one of them
191                    returns true (indicating that the event has been handled).
192                 */
193                 if (t) {
194                         r = true;
195                 }
196
197         }
198
199         return r;
200 }
201
202 bool
203 DragManager::have_item (ArdourCanvas::Item* i) const
204 {
205         list<Drag*>::const_iterator j = _drags.begin ();
206         while (j != _drags.end() && (*j)->item () != i) {
207                 ++j;
208         }
209
210         return j != _drags.end ();
211 }
212
213 Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
214         : _editor (e)
215         , _item (i)
216         , _pointer_frame_offset (0)
217         , _trackview_only (trackview_only)
218         , _move_threshold_passed (false)
219         , _was_double_click (false)
220         , _raw_grab_frame (0)
221         , _grab_frame (0)
222         , _last_pointer_frame (0)
223 {
224
225 }
226
227 void
228 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t /*time*/)
229 {
230         _item->ungrab ();
231         _item = new_item;
232
233         if (cursor == 0) {
234                 _item->grab ();
235         } else {
236                 _item->grab ();
237         }
238 }
239
240 void
241 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
242 {
243         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
244
245         if (Keyboard::is_button2_event (&event->button)) {
246                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
247                         _y_constrained = true;
248                         _x_constrained = false;
249                 } else {
250                         _y_constrained = false;
251                         _x_constrained = true;
252                 }
253         } else {
254                 _x_constrained = false;
255                 _y_constrained = false;
256         }
257
258         _raw_grab_frame = _editor->canvas_event_sample (event, &_grab_x, &_grab_y);
259         setup_pointer_frame_offset ();
260         _grab_frame = adjusted_frame (_raw_grab_frame, event);
261         _last_pointer_frame = _grab_frame;
262         _last_pointer_x = _grab_x;
263
264         if (_trackview_only) {
265                 _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
266         }
267
268         _last_pointer_y = _grab_y;
269
270         if (cursor == 0) {
271                 _item->grab ();
272         } else {
273                 /* CAIROCANVAS need a variant here that passes *cursor */
274                 _item->grab ();
275                 _editor->push_canvas_cursor (cursor);
276         }
277
278         if (_editor->session() && _editor->session()->transport_rolling()) {
279                 _was_rolling = true;
280         } else {
281                 _was_rolling = false;
282         }
283
284         switch (_editor->snap_type()) {
285         case SnapToRegionStart:
286         case SnapToRegionEnd:
287         case SnapToRegionSync:
288         case SnapToRegionBoundary:
289                 _editor->build_region_boundary_cache ();
290                 break;
291         default:
292                 break;
293         }
294 }
295
296 /** Call to end a drag `successfully'.  Ungrabs item and calls
297  *  subclass' finished() method.
298  *
299  *  @param event GDK event, or 0.
300  *  @return true if some movement occurred, otherwise false.
301  */
302 bool
303 Drag::end_grab (GdkEvent* event)
304 {
305         _editor->stop_canvas_autoscroll ();
306
307         _item->ungrab ();
308
309         finished (event, _move_threshold_passed);
310
311         _editor->verbose_cursor()->hide ();
312         _editor->pop_canvas_cursor ();
313
314         return _move_threshold_passed;
315 }
316
317 framepos_t
318 Drag::adjusted_frame (framepos_t f, GdkEvent const * event, bool snap) const
319 {
320         framepos_t pos = 0;
321
322         if (f > _pointer_frame_offset) {
323                 pos = f - _pointer_frame_offset;
324         }
325
326         if (snap) {
327                 _editor->snap_to_with_modifier (pos, event);
328         }
329
330         return pos;
331 }
332
333 framepos_t
334 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
335 {
336         return adjusted_frame (_drags->current_pointer_frame (), event, snap);
337 }
338
339 double
340 Drag::current_pointer_y () const
341 {
342         if (!_trackview_only) {
343                 return _drags->current_pointer_y ();
344         }
345         
346         return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
347 }
348
349 bool
350 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
351 {
352         /* check to see if we have moved in any way that matters since the last motion event */
353         if (_move_threshold_passed &&
354             (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
355             (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
356                 return false;
357         }
358
359         pair<framecnt_t, int> const threshold = move_threshold ();
360
361         bool const old_move_threshold_passed = _move_threshold_passed;
362
363         if (!from_autoscroll && !_move_threshold_passed) {
364
365                 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
366                 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
367
368                 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
369         }
370
371         if (active (_editor->mouse_mode) && _move_threshold_passed) {
372
373                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
374                         if (!from_autoscroll) {
375                                 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
376                         }
377
378                         if (!_editor->autoscroll_active() || from_autoscroll) {
379                                 motion (event, _move_threshold_passed != old_move_threshold_passed);
380                                 
381                                 _last_pointer_x = _drags->current_pointer_x ();
382                                 _last_pointer_y = current_pointer_y ();
383                                 _last_pointer_frame = adjusted_current_frame (event);
384                         }
385
386                         return true;
387                 }
388         }
389
390         return false;
391 }
392
393 /** Call to abort a drag.  Ungrabs item and calls subclass's aborted () */
394 void
395 Drag::abort ()
396 {
397         if (_item) {
398                 _item->ungrab ();
399         }
400
401         aborted (_move_threshold_passed);
402
403         _editor->stop_canvas_autoscroll ();
404         _editor->verbose_cursor()->hide ();
405 }
406
407 void
408 Drag::show_verbose_cursor_time (framepos_t frame)
409 {
410         /* We use DragManager::current_pointer_y() here 
411            because we need to position the verbose canvas
412            cursor within the overall canvas, regardless 
413            of this particular drag's _trackview_only
414            setting.
415         */
416            
417         _editor->verbose_cursor()->set_time (
418                 frame,
419                 _drags->current_pointer_x() + 10,
420                 _drags->current_pointer_y() + 10
421                 );
422
423         _editor->verbose_cursor()->show ();
424 }
425
426 void
427 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
428 {
429         _editor->verbose_cursor()->show (xoffset);
430
431         /* We use DragManager::current_pointer_y() here 
432            because we need to position the verbose canvas
433            cursor within the overall canvas, regardless 
434            of this particular drag's _trackview_only
435            setting.
436         */
437
438         _editor->verbose_cursor()->set_duration (
439                 start, end,
440                 _drags->current_pointer_x() + 10,
441                 _drags->current_pointer_y() + 10
442                 );
443 }
444
445 void
446 Drag::show_verbose_cursor_text (string const & text)
447 {
448         _editor->verbose_cursor()->show ();
449
450         /* We use DragManager::current_pointer_y() here 
451            because we need to position the verbose canvas
452            cursor within the overall canvas, regardless 
453            of this particular drag's _trackview_only
454            setting.
455         */
456
457         _editor->verbose_cursor()->set (
458                 text,
459                 _drags->current_pointer_x() + 10,
460                 _drags->current_pointer_y() + 10
461                 );
462 }
463
464 boost::shared_ptr<Region>
465 Drag::add_midi_region (MidiTimeAxisView* view)
466 {
467         if (_editor->session()) {
468                 const TempoMap& map (_editor->session()->tempo_map());
469                 framecnt_t pos = grab_frame();
470                 const Meter& m = map.meter_at (pos);
471                 /* not that the frame rate used here can be affected by pull up/down which
472                    might be wrong.
473                 */
474                 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
475                 return view->add_region (grab_frame(), len, true);
476         }
477
478         return boost::shared_ptr<Region>();
479 }
480
481 struct EditorOrderTimeAxisViewSorter {
482     bool operator() (TimeAxisView* a, TimeAxisView* b) {
483             RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
484             RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
485             assert (ra && rb);
486             return ra->route()->order_key () < rb->route()->order_key ();
487     }
488 };
489
490 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
491         : Drag (e, i),
492           _primary (p)
493 {
494         _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
495
496         /* Make a list of tracks to refer to during the drag; we include hidden tracks,
497            as some of the regions we are dragging may be on such tracks.
498         */
499
500         TrackViewList track_views = _editor->track_views;
501         track_views.sort (EditorOrderTimeAxisViewSorter ());
502
503         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
504                 _time_axis_views.push_back (*i);
505                 
506                 TimeAxisView::Children children_list = (*i)->get_child_list ();
507                 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
508                         _time_axis_views.push_back (j->get());
509                 }
510         }
511
512         /* the list of views can be empty at this point if this is a region list-insert drag
513          */
514
515         for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
516                 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
517         }
518
519         RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
520 }
521
522 void
523 RegionDrag::region_going_away (RegionView* v)
524 {
525         list<DraggingView>::iterator i = _views.begin ();
526         while (i != _views.end() && i->view != v) {
527                 ++i;
528         }
529
530         if (i != _views.end()) {
531                 _views.erase (i);
532         }
533 }
534
535 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
536  *  or -1 if it is not found.
537  */
538 int
539 RegionDrag::find_time_axis_view (TimeAxisView* t) const
540 {
541         int i = 0;
542         int const N = _time_axis_views.size ();
543         while (i < N && _time_axis_views[i] != t) {
544                 ++i;
545         }
546
547         if (i == N) {
548                 return -1;
549         }
550
551         return i;
552 }
553
554 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
555         : RegionDrag (e, i, p, v)
556         , _brushing (b)
557         , _total_x_delta (0)
558         , _last_pointer_time_axis_view (0)
559         , _last_pointer_layer (0)
560 {
561         DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
562 }
563
564 void
565 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
566 {
567         Drag::start_grab (event, cursor);
568
569         show_verbose_cursor_time (_last_frame_position);
570
571         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
572         if (tv.first) {
573                 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
574                 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
575         }
576 }
577
578 double
579 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
580 {
581         /* compute the amount of pointer motion in frames, and where
582            the region would be if we moved it by that much.
583         */
584         *pending_region_position = adjusted_current_frame (event);
585
586         framepos_t sync_frame;
587         framecnt_t sync_offset;
588         int32_t sync_dir;
589
590         sync_offset = _primary->region()->sync_offset (sync_dir);
591
592         /* we don't handle a sync point that lies before zero.
593          */
594         if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
595
596                 sync_frame = *pending_region_position + (sync_dir*sync_offset);
597
598                 _editor->snap_to_with_modifier (sync_frame, event);
599
600                 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
601
602         } else {
603                 *pending_region_position = _last_frame_position;
604         }
605
606         if (*pending_region_position > max_framepos - _primary->region()->length()) {
607                 *pending_region_position = _last_frame_position;
608         }
609
610         double dx = 0;
611
612         /* in locked edit mode, reverse the usual meaning of _x_constrained */
613         bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
614
615         if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
616
617                 /* x movement since last time (in pixels) */
618                 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
619
620                 /* total x movement */
621                 framecnt_t total_dx = *pending_region_position;
622                 if (regions_came_from_canvas()) {
623                         total_dx = total_dx - grab_frame ();
624                 }
625
626                 /* check that no regions have gone off the start of the session */
627                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
628                         if ((i->view->region()->position() + total_dx) < 0) {
629                                 dx = 0;
630                                 *pending_region_position = _last_frame_position;
631                                 break;
632                         }
633                 }
634
635                 _last_frame_position = *pending_region_position;
636         }
637
638         return dx;
639 }
640
641 bool
642 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
643 {
644         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
645                 int const n = i->time_axis_view + delta_track;
646                 if (n < 0 || n >= int (_time_axis_views.size())) {
647                         /* off the top or bottom track */
648                         return false;
649                 }
650
651                 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
652                 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
653                         /* not a track, or the wrong type */
654                         return false;
655                 }
656
657                 double const l = i->layer + delta_layer;
658
659                 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
660                    mode to allow the user to place a region below another on layer 0.
661                 */
662                 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
663                         /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
664                            If it has, the layers will be munged later anyway, so it's ok.
665                         */
666                         return false;
667                 }
668         }
669
670         /* all regions being dragged are ok with this change */
671         return true;
672 }
673
674 void
675 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
676 {
677         double delta_layer = 0;
678         int delta_time_axis_view = 0;
679
680         assert (!_views.empty ());
681
682         /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
683
684         /* Find the TimeAxisView that the pointer is now over */
685         pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
686         TimeAxisView* tv = r.first;
687
688         if (tv && tv->view()) {
689                 double layer = r.second;
690
691                 if (first_move && tv->view()->layer_display() == Stacked) {
692                         tv->view()->set_layer_display (Expanded);
693                 }
694
695                 /* Here's the current pointer position in terms of time axis view and layer */
696                 int const current_pointer_time_axis_view = find_time_axis_view (tv);
697                 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
698                 
699                 /* Work out the change in y */
700
701                 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
702                 delta_layer = current_pointer_layer - _last_pointer_layer;
703         } 
704         
705         /* Work out the change in x */
706         framepos_t pending_region_position;
707         double const x_delta = compute_x_delta (event, &pending_region_position);
708
709         /* Verify change in y */
710         if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
711                 /* this y movement is not allowed, so do no y movement this time */
712                 delta_time_axis_view = 0;
713                 delta_layer = 0;
714         }
715
716         if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
717                 /* haven't reached next snap point, and we're not switching
718                    trackviews nor layers. nothing to do.
719                 */
720                 return;
721         }
722
723         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
724
725         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
726
727                 RegionView* rv = i->view;
728
729                 if (rv->region()->locked() || rv->region()->video_locked()) {
730                         continue;
731                 }
732
733                 if (first_move) {
734                         rv->drag_start (); 
735
736                         /* reparent the regionview into a group above all
737                          * others
738                          */
739                         
740                         ArdourCanvas::Item* rvg = rv->get_canvas_group();
741                         Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
742                         Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
743                         rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
744                         /* move the item so that it continues to appear at the
745                            same location now that its parent has changed.
746                         */
747                         rvg->move (rv_canvas_offset - dmg_canvas_offset);
748                 }
749
750                 /* If we have moved tracks, we'll fudge the layer delta so that the
751                    region gets moved back onto layer 0 on its new track; this avoids
752                    confusion when dragging regions from non-zero layers onto different
753                    tracks.
754                 */
755                 double this_delta_layer = delta_layer;
756                 if (delta_time_axis_view != 0) {
757                         this_delta_layer = - i->layer;
758                 }
759
760                 if (tv) {
761
762                         int track_index;
763                         
764                         if (i->time_axis_view >= 0) {
765                                 track_index = i->time_axis_view + delta_time_axis_view;
766                         } else {
767                                 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
768                         }
769                          
770                         if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
771                                 continue;
772                         }
773
774                         /* The TimeAxisView that this region is now over */
775                         TimeAxisView* current_tv = _time_axis_views[track_index];
776
777                         /* Ensure it is moved from stacked -> expanded if appropriate */
778                         if (current_tv->view()->layer_display() == Stacked) {
779                                 current_tv->view()->set_layer_display (Expanded);
780                         }
781                 
782                         /* We're only allowed to go -ve in layer on Expanded views */
783                         if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
784                                 this_delta_layer = - i->layer;
785                         }
786                 
787                         /* Set height */
788                         rv->set_height (current_tv->view()->child_height ());
789                 
790                         /* Update show/hidden status as the region view may have come from a hidden track,
791                            or have moved to one.
792                         */
793                         if (current_tv->hidden ()) {
794                                 rv->get_canvas_group()->hide ();
795                         } else {
796                                 rv->get_canvas_group()->show ();
797                         }
798
799                         /* Update the DraggingView */
800                         i->time_axis_view = track_index;
801                         i->layer += this_delta_layer;
802
803                         if (_brushing) {
804                                 _editor->mouse_brush_insert_region (rv, pending_region_position);
805                         } else {
806                                 Duple track_origin;
807
808                                 /* Get the y coordinate of the top of the track that this region is now over */
809                                 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
810                                 
811                                 /* And adjust for the layer that it should be on */
812                                 StreamView* cv = current_tv->view ();
813                                 switch (cv->layer_display ()) {
814                                 case Overlaid:
815                                         break;
816                                 case Stacked:
817                                         track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
818                                         break;
819                                 case Expanded:
820                                         track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
821                                         break;
822                                 }
823
824                                 /* need to get the parent of the regionview
825                                  * canvas group and get its position in
826                                  * equivalent coordinate space as the trackview
827                                  * we are now dragging over.
828                                  */
829                                 
830                                 /* Now move the region view */
831                                 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
832                         }
833                 } else {
834
835                         /* Only move the region into the empty dropzone at the bottom if the pointer
836                          * is down there.
837                          */
838
839                         if (current_pointer_y() >= 0) {
840                                 
841                                 Coord last_track_bottom_edge;
842                                 if (!_time_axis_views.empty()) {
843                                         TimeAxisView* last = _time_axis_views.back();
844                                         last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
845                                 } else {
846                                         last_track_bottom_edge = 0;
847                                 }
848
849                                 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
850                                 i->time_axis_view = -1;
851                         }
852                 }
853                 
854         } /* foreach region */
855
856         _total_x_delta += x_delta;
857
858         if (x_delta != 0 && !_brushing) {
859                 show_verbose_cursor_time (_last_frame_position);
860         }
861
862         _last_pointer_time_axis_view += delta_time_axis_view;
863         _last_pointer_layer += delta_layer;
864 }
865
866 void
867 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
868 {
869         if (_copy && first_move) {
870
871                 /* duplicate the regionview(s) and region(s) */
872
873                 list<DraggingView> new_regionviews;
874
875                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
876
877                         RegionView* rv = i->view;
878                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
879                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
880
881                         const boost::shared_ptr<const Region> original = rv->region();
882                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
883                         region_copy->set_position (original->position());
884
885                         RegionView* nrv;
886                         if (arv) {
887                                 boost::shared_ptr<AudioRegion> audioregion_copy
888                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
889
890                                 nrv = new AudioRegionView (*arv, audioregion_copy);
891                         } else if (mrv) {
892                                 boost::shared_ptr<MidiRegion> midiregion_copy
893                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
894                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
895                         } else {
896                                 continue;
897                         }
898
899                         nrv->get_canvas_group()->show ();
900                         new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
901
902                         /* swap _primary to the copy */
903
904                         if (rv == _primary) {
905                                 _primary = nrv;
906                         }
907
908                         /* ..and deselect the one we copied */
909
910                         rv->set_selected (false);
911                 }
912
913                 if (!new_regionviews.empty()) {
914
915                         /* reflect the fact that we are dragging the copies */
916
917                         _views = new_regionviews;
918
919                         swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
920                 }
921         }
922
923         RegionMotionDrag::motion (event, first_move);
924 }
925
926 void
927 RegionMotionDrag::finished (GdkEvent *, bool)
928 {
929         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
930                 if (!(*i)->view()) {
931                         continue;
932                 }
933
934                 if ((*i)->view()->layer_display() == Expanded) {
935                         (*i)->view()->set_layer_display (Stacked);
936                 }
937         }
938 }
939
940 void
941 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
942 {
943         RegionMotionDrag::finished (ev, movement_occurred);
944         
945         if (!movement_occurred) {
946                 
947                 /* just a click */
948
949                 if (was_double_click() && !_views.empty()) {
950                         DraggingView dv = _views.front();
951                         dv.view->show_region_editor ();
952                         
953                 }
954
955                 return;
956         }
957
958         /* reverse this here so that we have the correct logic to finalize
959            the drag.
960         */
961
962         if (Config->get_edit_mode() == Lock) {
963                 _x_constrained = !_x_constrained;
964         }
965
966         assert (!_views.empty ());
967
968         /* We might have hidden region views so that they weren't visible during the drag
969            (when they have been reparented).  Now everything can be shown again, as region
970            views are back in their track parent groups.
971         */
972         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
973                 i->view->get_canvas_group()->show ();
974         }
975         
976         bool const changed_position = (_last_frame_position != _primary->region()->position());
977         bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
978         framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
979         
980         if (_copy) {
981
982                 finished_copy (
983                         changed_position,
984                         changed_tracks,
985                         drag_delta
986                         );
987
988         } else {
989
990                 finished_no_copy (
991                         changed_position,
992                         changed_tracks,
993                         drag_delta
994                         );
995
996         }
997
998         _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
999 }
1000
1001 RouteTimeAxisView*
1002 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
1003 {                       
1004         /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
1005            new track.
1006          */
1007                         
1008         try {
1009                 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
1010                         list<boost::shared_ptr<AudioTrack> > audio_tracks;
1011                         audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
1012                         RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
1013                         if (rtav) {
1014                                 rtav->set_height (original->current_height());
1015                         }
1016                         return rtav;
1017                 } else {
1018                         ChanCount one_midi_port (DataType::MIDI, 1);
1019                         list<boost::shared_ptr<MidiTrack> > midi_tracks;
1020                         midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1021                         RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1022                         if (rtav) {
1023                                 rtav->set_height (original->current_height());
1024                         }
1025                         return rtav;
1026                 }                                               
1027         } catch (...) {
1028                 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1029                 return 0;
1030         }
1031 }
1032
1033 void
1034 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1035 {
1036         RegionSelection new_views;
1037         PlaylistSet modified_playlists;
1038         RouteTimeAxisView* new_time_axis_view = 0;      
1039
1040         if (_brushing) {
1041                 /* all changes were made during motion event handlers */
1042
1043                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1044                         delete i->view;
1045                 }
1046
1047                 _editor->commit_reversible_command ();
1048                 return;
1049         }
1050
1051         if (_x_constrained) {
1052                 _editor->begin_reversible_command (Operations::fixed_time_region_copy);
1053         } else {
1054                 _editor->begin_reversible_command (Operations::region_copy);
1055         }
1056
1057         /* insert the regions into their new playlists */
1058         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1059
1060                 RouteTimeAxisView* dest_rtv = 0;
1061
1062                 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1063                         continue;
1064                 }
1065
1066                 framepos_t where;
1067
1068                 if (changed_position && !_x_constrained) {
1069                         where = i->view->region()->position() - drag_delta;
1070                 } else {
1071                         where = i->view->region()->position();
1072                 }
1073                 
1074                 if (i->time_axis_view < 0) {
1075                         if (!new_time_axis_view) {
1076                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1077                         }
1078                         dest_rtv = new_time_axis_view;
1079                 } else {
1080                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1081                 }               
1082                 
1083                 if (dest_rtv != 0) {
1084                         RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1085                         if (new_view != 0) {
1086                                 new_views.push_back (new_view);
1087                         }
1088                 }
1089         
1090                 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1091                    since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1092                  */
1093
1094                 list<DraggingView>::const_iterator next = i;
1095                 ++next;
1096                 delete i->view;
1097                 i = next;
1098         }
1099
1100         /* If we've created new regions either by copying or moving
1101            to a new track, we want to replace the old selection with the new ones
1102         */
1103
1104         if (new_views.size() > 0) {
1105                 _editor->selection->set (new_views);
1106         }
1107
1108         /* write commands for the accumulated diffs for all our modified playlists */
1109         add_stateful_diff_commands_for_playlists (modified_playlists);
1110
1111         _editor->commit_reversible_command ();
1112 }
1113
1114 void
1115 RegionMoveDrag::finished_no_copy (
1116         bool const changed_position,
1117         bool const changed_tracks,
1118         framecnt_t const drag_delta
1119         )
1120 {
1121         RegionSelection new_views;
1122         PlaylistSet modified_playlists;
1123         PlaylistSet frozen_playlists;
1124         set<RouteTimeAxisView*> views_to_update;
1125         RouteTimeAxisView* new_time_axis_view = 0;
1126
1127         if (_brushing) {
1128                 /* all changes were made during motion event handlers */
1129                 _editor->commit_reversible_command ();
1130                 return;
1131         }
1132
1133         if (_x_constrained) {
1134                 _editor->begin_reversible_command (_("fixed time region drag"));
1135         } else {
1136                 _editor->begin_reversible_command (Operations::region_drag);
1137         }
1138
1139         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1140
1141                 RegionView* rv = i->view;
1142                 RouteTimeAxisView* dest_rtv = 0;
1143
1144                 if (rv->region()->locked() || rv->region()->video_locked()) {
1145                         ++i;
1146                         continue;
1147                 }
1148                 
1149                 if (i->time_axis_view < 0) {
1150                         if (!new_time_axis_view) {
1151                                 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1152                         }
1153                         dest_rtv = new_time_axis_view;
1154                 } else {
1155                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1156                 }
1157                         
1158                 assert (dest_rtv);
1159
1160                 double const dest_layer = i->layer;
1161                 
1162                 views_to_update.insert (dest_rtv);
1163
1164                 framepos_t where;
1165
1166                 if (changed_position && !_x_constrained) {
1167                         where = rv->region()->position() - drag_delta;
1168                 } else {
1169                         where = rv->region()->position();
1170                 }
1171
1172                 if (changed_tracks) {
1173
1174                         /* insert into new playlist */
1175
1176                         RegionView* new_view = insert_region_into_playlist (
1177                                 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1178                                 );
1179
1180                         if (new_view == 0) {
1181                                 ++i;
1182                                 continue;
1183                         }
1184
1185                         new_views.push_back (new_view);
1186
1187                         /* remove from old playlist */
1188
1189                         /* the region that used to be in the old playlist is not
1190                            moved to the new one - we use a copy of it. as a result,
1191                            any existing editor for the region should no longer be
1192                            visible.
1193                         */
1194                         rv->hide_region_editor();
1195
1196
1197                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1198
1199                 } else {
1200
1201                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1202
1203                         /* this movement may result in a crossfade being modified, or a layering change,
1204                            so we need to get undo data from the playlist as well as the region.
1205                         */
1206
1207                         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1208                         if (r.second) {
1209                                 playlist->clear_changes ();
1210                         }
1211
1212                         rv->region()->clear_changes ();
1213
1214                         /*
1215                            motion on the same track. plonk the previously reparented region
1216                            back to its original canvas group (its streamview).
1217                            No need to do anything for copies as they are fake regions which will be deleted.
1218                         */
1219
1220                         rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1221                         rv->get_canvas_group()->set_y_position (i->initial_y);
1222                         rv->drag_end ();
1223
1224                         /* just change the model */
1225                         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1226                                 playlist->set_layer (rv->region(), dest_layer);
1227                         }
1228
1229                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1230
1231                         r = frozen_playlists.insert (playlist);
1232
1233                         if (r.second) {
1234                                 playlist->freeze ();
1235                         }
1236
1237                         rv->region()->set_position (where);
1238
1239                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1240                 }
1241
1242                 if (changed_tracks) {
1243
1244                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1245                            was selected in all of them, then removing it from a playlist will have removed all
1246                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1247                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1248                            corresponding regionview, and _views is now empty).
1249
1250                            This could have invalidated any and all iterators into _views.
1251
1252                            The heuristic we use here is: if the region selection is empty, break out of the loop
1253                            here. if the region selection is not empty, then restart the loop because we know that
1254                            we must have removed at least the region(view) we've just been working on as well as any
1255                            that we processed on previous iterations.
1256
1257                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1258                            we can just iterate.
1259                         */
1260
1261
1262                         if (_views.empty()) {
1263                                 break;
1264                         } else {
1265                                 i = _views.begin();
1266                         }
1267
1268                 } else {
1269                         ++i;
1270                 }
1271         }
1272
1273         /* If we've created new regions either by copying or moving
1274            to a new track, we want to replace the old selection with the new ones
1275         */
1276
1277         if (new_views.size() > 0) {
1278                 _editor->selection->set (new_views);
1279         }
1280
1281         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1282                 (*p)->thaw();
1283         }
1284
1285         /* write commands for the accumulated diffs for all our modified playlists */
1286         add_stateful_diff_commands_for_playlists (modified_playlists);
1287
1288         _editor->commit_reversible_command ();
1289
1290         /* We have futzed with the layering of canvas items on our streamviews.
1291            If any region changed layer, this will have resulted in the stream
1292            views being asked to set up their region views, and all will be well.
1293            If not, we might now have badly-ordered region views.  Ask the StreamViews
1294            involved to sort themselves out, just in case.
1295         */
1296
1297         for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1298                 (*i)->view()->playlist_layered ((*i)->track ());
1299         }
1300 }
1301
1302 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1303  *  @param region Region to remove.
1304  *  @param playlist playlist To remove from.
1305  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1306  *  that clear_changes () is only called once per playlist.
1307  */
1308 void
1309 RegionMoveDrag::remove_region_from_playlist (
1310         boost::shared_ptr<Region> region,
1311         boost::shared_ptr<Playlist> playlist,
1312         PlaylistSet& modified_playlists
1313         )
1314 {
1315         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1316
1317         if (r.second) {
1318                 playlist->clear_changes ();
1319         }
1320
1321         playlist->remove_region (region);
1322 }
1323
1324
1325 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1326  *  clearing the playlist's diff history first if necessary.
1327  *  @param region Region to insert.
1328  *  @param dest_rtv Destination RouteTimeAxisView.
1329  *  @param dest_layer Destination layer.
1330  *  @param where Destination position.
1331  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1332  *  that clear_changes () is only called once per playlist.
1333  *  @return New RegionView, or 0 if no insert was performed.
1334  */
1335 RegionView *
1336 RegionMoveDrag::insert_region_into_playlist (
1337         boost::shared_ptr<Region> region,
1338         RouteTimeAxisView* dest_rtv,
1339         layer_t dest_layer,
1340         framecnt_t where,
1341         PlaylistSet& modified_playlists
1342         )
1343 {
1344         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1345         if (!dest_playlist) {
1346                 return 0;
1347         }
1348
1349         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1350         _new_region_view = 0;
1351         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1352
1353         /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1354         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1355         if (r.second) {
1356                 dest_playlist->clear_changes ();
1357         }
1358
1359         dest_playlist->add_region (region, where);
1360
1361         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1362                 dest_playlist->set_layer (region, dest_layer);
1363         }
1364
1365         c.disconnect ();
1366
1367         assert (_new_region_view);
1368
1369         return _new_region_view;
1370 }
1371
1372 void
1373 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1374 {
1375         _new_region_view = rv;
1376 }
1377
1378 void
1379 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1380 {
1381         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1382                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1383                 if (!c->empty()) {
1384                         _editor->session()->add_command (c);
1385                 } else {
1386                         delete c;
1387                 }
1388         }
1389 }
1390
1391
1392 void
1393 RegionMoveDrag::aborted (bool movement_occurred)
1394 {
1395         if (_copy) {
1396
1397                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1398                         delete i->view;
1399                 }
1400
1401                 _views.clear ();
1402
1403         } else {
1404                 RegionMotionDrag::aborted (movement_occurred);
1405         }
1406 }
1407
1408 void
1409 RegionMotionDrag::aborted (bool)
1410 {
1411         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1412
1413                 StreamView* sview = (*i)->view();
1414
1415                 if (sview) {
1416                         if (sview->layer_display() == Expanded) {
1417                                 sview->set_layer_display (Stacked);
1418                         }
1419                 }
1420         }
1421         
1422         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1423                 RegionView* rv = i->view;
1424                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1425                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1426                 assert (rtv);
1427                 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1428                 rv->get_canvas_group()->set_y_position (0);
1429                 rv->drag_end ();
1430                 rv->move (-_total_x_delta, 0);
1431                 rv->set_height (rtv->view()->child_height ());
1432         }
1433 }
1434
1435 /** @param b true to brush, otherwise false.
1436  *  @param c true to make copies of the regions being moved, otherwise false.
1437  */
1438 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1439         : RegionMotionDrag (e, i, p, v, b),
1440           _copy (c)
1441 {
1442         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1443
1444         double speed = 1;
1445         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1446         if (rtv && rtv->is_track()) {
1447                 speed = rtv->track()->speed ();
1448         }
1449
1450         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1451 }
1452
1453 void
1454 RegionMoveDrag::setup_pointer_frame_offset ()
1455 {
1456         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1457 }
1458
1459 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1460         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1461 {
1462         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1463
1464         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1465                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1466
1467         _primary = v->view()->create_region_view (r, false, false);
1468
1469         _primary->get_canvas_group()->show ();
1470         _primary->set_position (pos, 0);
1471         _views.push_back (DraggingView (_primary, this, v));
1472
1473         _last_frame_position = pos;
1474
1475         _item = _primary->get_canvas_group ();
1476 }
1477
1478 void
1479 RegionInsertDrag::finished (GdkEvent *, bool)
1480 {
1481         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1482
1483         _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1484         _primary->get_canvas_group()->set_y_position (0);
1485
1486         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1487
1488         _editor->begin_reversible_command (Operations::insert_region);
1489         playlist->clear_changes ();
1490         playlist->add_region (_primary->region (), _last_frame_position);
1491         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1492         _editor->commit_reversible_command ();
1493
1494         delete _primary;
1495         _primary = 0;
1496         _views.clear ();
1497 }
1498
1499 void
1500 RegionInsertDrag::aborted (bool)
1501 {
1502         delete _primary;
1503         _primary = 0;
1504         _views.clear ();
1505 }
1506
1507 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1508         : RegionMoveDrag (e, i, p, v, false, false)
1509 {
1510         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1511 }
1512
1513 struct RegionSelectionByPosition {
1514     bool operator() (RegionView*a, RegionView* b) {
1515             return a->region()->position () < b->region()->position();
1516     }
1517 };
1518
1519 void
1520 RegionSpliceDrag::motion (GdkEvent* event, bool)
1521 {
1522         /* Which trackview is this ? */
1523
1524         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1525         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1526
1527         /* The region motion is only processed if the pointer is over
1528            an audio track.
1529         */
1530
1531         if (!tv || !tv->is_track()) {
1532                 /* To make sure we hide the verbose canvas cursor when the mouse is
1533                    not held over and audiotrack.
1534                 */
1535                 _editor->verbose_cursor()->hide ();
1536                 return;
1537         }
1538
1539         int dir;
1540
1541         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1542                 dir = 1;
1543         } else {
1544                 dir = -1;
1545         }
1546
1547         RegionSelection copy (_editor->selection->regions);
1548
1549         RegionSelectionByPosition cmp;
1550         copy.sort (cmp);
1551
1552         framepos_t const pf = adjusted_current_frame (event);
1553
1554         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1555
1556                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1557
1558                 if (!atv) {
1559                         continue;
1560                 }
1561
1562                 boost::shared_ptr<Playlist> playlist;
1563
1564                 if ((playlist = atv->playlist()) == 0) {
1565                         continue;
1566                 }
1567
1568                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1569                         continue;
1570                 }
1571
1572                 if (dir > 0) {
1573                         if (pf < (*i)->region()->last_frame() + 1) {
1574                                 continue;
1575                         }
1576                 } else {
1577                         if (pf > (*i)->region()->first_frame()) {
1578                                 continue;
1579                         }
1580                 }
1581
1582
1583                 playlist->shuffle ((*i)->region(), dir);
1584         }
1585 }
1586
1587 void
1588 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1589 {
1590         RegionMoveDrag::finished (event, movement_occurred);
1591 }
1592
1593 void
1594 RegionSpliceDrag::aborted (bool)
1595 {
1596         /* XXX: TODO */
1597 }
1598
1599 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1600         : Drag (e, i),
1601           _view (dynamic_cast<MidiTimeAxisView*> (v))
1602 {
1603         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1604
1605         assert (_view);
1606 }
1607
1608 void
1609 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1610 {
1611         if (first_move) {
1612                 _region = add_midi_region (_view);
1613                 _view->playlist()->freeze ();
1614         } else {
1615                 if (_region) {
1616                         framepos_t const f = adjusted_current_frame (event);
1617                         if (f < grab_frame()) {
1618                                 _region->set_position (f);
1619                         }
1620
1621                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1622                            so that if this region is duplicated, its duplicate starts on
1623                            a snap point rather than 1 frame after a snap point.  Otherwise things get
1624                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
1625                            place snapped notes at the start of the region.
1626                         */
1627
1628                         framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1629                         _region->set_length (len < 1 ? 1 : len);
1630                 }
1631         }
1632 }
1633
1634 void
1635 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1636 {
1637         if (!movement_occurred) {
1638                 add_midi_region (_view);
1639         } else {
1640                 _view->playlist()->thaw ();
1641         }
1642 }
1643
1644 void
1645 RegionCreateDrag::aborted (bool)
1646 {
1647         if (_region) {
1648                 _view->playlist()->thaw ();
1649         }
1650
1651         /* XXX */
1652 }
1653
1654 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1655         : Drag (e, i)
1656         , region (0)
1657 {
1658         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1659 }
1660
1661 void
1662 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1663 {
1664         Gdk::Cursor* cursor;
1665         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1666         assert (cnote);
1667         float x_fraction = cnote->mouse_x_fraction ();
1668
1669         if (x_fraction > 0.0 && x_fraction < 0.25) {
1670                 cursor = _editor->cursors()->left_side_trim;
1671         } else  {
1672                 cursor = _editor->cursors()->right_side_trim;
1673         }
1674
1675         Drag::start_grab (event, cursor);
1676
1677         region = &cnote->region_view();
1678
1679         double const region_start = region->get_position_pixels();
1680         double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1681
1682         if (grab_x() <= middle_point) {
1683                 cursor = _editor->cursors()->left_side_trim;
1684                 at_front = true;
1685         } else {
1686                 cursor = _editor->cursors()->right_side_trim;
1687                 at_front = false;
1688         }
1689
1690         _item->grab ();
1691
1692         if (event->motion.state & Keyboard::PrimaryModifier) {
1693                 relative = false;
1694         } else {
1695                 relative = true;
1696         }
1697
1698         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1699
1700         if (ms.size() > 1) {
1701                 /* has to be relative, may make no sense otherwise */
1702                 relative = true;
1703         }
1704
1705         /* select this note; if it is already selected, preserve the existing selection,
1706            otherwise make this note the only one selected.
1707         */
1708         region->note_selected (cnote, cnote->selected ());
1709
1710         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1711                 MidiRegionSelection::iterator next;
1712                 next = r;
1713                 ++next;
1714                 (*r)->begin_resizing (at_front);
1715                 r = next;
1716         }
1717 }
1718
1719 void
1720 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1721 {
1722         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1723         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1724                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1725                 assert (nb);
1726                 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1727         }
1728 }
1729
1730 void
1731 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1732 {
1733         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1734         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1735                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1736                 assert (nb);
1737                 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1738         }
1739 }
1740
1741 void
1742 NoteResizeDrag::aborted (bool)
1743 {
1744         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1745         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1746                 (*r)->abort_resizing ();
1747         }
1748 }
1749
1750 AVDraggingView::AVDraggingView (RegionView* v)
1751         : view (v)
1752 {
1753         initial_position = v->region()->position ();
1754 }
1755
1756 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1757         : Drag (e, i)
1758 {
1759         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1760
1761         RegionSelection rs;
1762         TrackViewList empty;
1763         empty.clear();
1764         _editor->get_regions_after(rs, (framepos_t) 0, empty);
1765         std::list<RegionView*> views = rs.by_layer();
1766
1767         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1768                 RegionView* rv = (*i);
1769                 if (!rv->region()->video_locked()) {
1770                         continue;
1771                 }
1772                 _views.push_back (AVDraggingView (rv));
1773         }
1774 }
1775
1776 void
1777 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1778 {
1779         Drag::start_grab (event);
1780         if (_editor->session() == 0) {
1781                 return;
1782         }
1783
1784         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1785         _max_backwards_drag = (
1786                           ARDOUR_UI::instance()->video_timeline->get_duration()
1787                         + ARDOUR_UI::instance()->video_timeline->get_offset()
1788                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1789                         );
1790
1791         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1792                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1793                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1794                 }
1795         }
1796         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1797
1798         char buf[128];
1799         Timecode::Time timecode;
1800         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1801         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);
1802         _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1803         _editor->verbose_cursor()->show ();
1804 }
1805
1806 void
1807 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1808 {
1809         if (_editor->session() == 0) {
1810                 return;
1811         }
1812         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1813                 return;
1814         }
1815
1816         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1817         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1818
1819         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1820                 dt = - _max_backwards_drag;
1821         }
1822
1823         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1824         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1825
1826         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1827                 RegionView* rv = i->view;
1828                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1829                 if (first_move) {
1830                         rv->drag_start ();
1831                         rv->region()->clear_changes ();
1832                         rv->region()->suspend_property_changes();
1833                 }
1834                 rv->region()->set_position(i->initial_position + dt);
1835                 rv->region_changed(ARDOUR::Properties::position);
1836         }
1837
1838         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1839         Timecode::Time timecode;
1840         Timecode::Time timediff;
1841         char buf[128];
1842         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1843         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1844         snprintf (buf, sizeof (buf),
1845                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1846                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1847                         , _("Video Start:"),
1848                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1849                         , _("Diff:"),
1850                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1851                                 );
1852         _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1853         _editor->verbose_cursor()->show ();
1854 }
1855
1856 void
1857 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1858 {
1859         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1860                 return;
1861         }
1862
1863         if (!movement_occurred || ! _editor->session()) {
1864                 return;
1865         }
1866
1867         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1868
1869         _editor->begin_reversible_command (_("Move Video"));
1870
1871         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1872         ARDOUR_UI::instance()->video_timeline->save_undo();
1873         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1874         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1875
1876         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1877                 i->view->drag_end();
1878                 i->view->region()->resume_property_changes ();
1879
1880                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1881         }
1882
1883         _editor->session()->maybe_update_session_range(
1884                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1885                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1886                         );
1887
1888
1889         _editor->commit_reversible_command ();
1890 }
1891
1892 void
1893 VideoTimeLineDrag::aborted (bool)
1894 {
1895         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1896                 return;
1897         }
1898         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1899         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1900
1901         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1902                 i->view->region()->resume_property_changes ();
1903                 i->view->region()->set_position(i->initial_position);
1904         }
1905 }
1906
1907 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1908         : RegionDrag (e, i, p, v)
1909         , _preserve_fade_anchor (preserve_fade_anchor)
1910         , _jump_position_when_done (false)
1911 {
1912         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1913 }
1914
1915 void
1916 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1917 {
1918         double speed = 1.0;
1919         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1920         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1921
1922         if (tv && tv->is_track()) {
1923                 speed = tv->track()->speed();
1924         }
1925
1926         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1927         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1928         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1929
1930         framepos_t const pf = adjusted_current_frame (event);
1931
1932         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1933                 /* Move the contents of the region around without changing the region bounds */
1934                 _operation = ContentsTrim;
1935                 Drag::start_grab (event, _editor->cursors()->trimmer);
1936         } else {
1937                 /* These will get overridden for a point trim.*/
1938                 if (pf < (region_start + region_length/2)) {
1939                         /* closer to front */
1940                         _operation = StartTrim;
1941
1942                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1943                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
1944                         } else {
1945                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
1946                         }
1947                 } else {
1948                         /* closer to end */
1949                         _operation = EndTrim;
1950                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1951                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
1952                         } else {
1953                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
1954                         }
1955                 }
1956         }
1957
1958         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1959                 _jump_position_when_done = true;
1960         }
1961
1962         switch (_operation) {
1963         case StartTrim:
1964                 show_verbose_cursor_time (region_start);
1965                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1966                         i->view->trim_front_starting ();
1967                 }
1968                 break;
1969         case EndTrim:
1970                 show_verbose_cursor_time (region_end);
1971                 break;
1972         case ContentsTrim:
1973                 show_verbose_cursor_time (pf);
1974                 break;
1975         }
1976
1977         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1978                 i->view->region()->suspend_property_changes ();
1979         }
1980 }
1981
1982 void
1983 TrimDrag::motion (GdkEvent* event, bool first_move)
1984 {
1985         RegionView* rv = _primary;
1986
1987         double speed = 1.0;
1988         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1989         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1990         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1991         frameoffset_t frame_delta = 0;
1992
1993         if (tv && tv->is_track()) {
1994                 speed = tv->track()->speed();
1995         }
1996
1997         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1998
1999         if (first_move) {
2000
2001                 string trim_type;
2002
2003                 switch (_operation) {
2004                 case StartTrim:
2005                         trim_type = "Region start trim";
2006                         break;
2007                 case EndTrim:
2008                         trim_type = "Region end trim";
2009                         break;
2010                 case ContentsTrim:
2011                         trim_type = "Region content trim";
2012                         break;
2013                 default:
2014                         assert(0);
2015                         break;
2016                 }
2017
2018                 _editor->begin_reversible_command (trim_type);
2019
2020                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2021                         RegionView* rv = i->view;
2022                         rv->enable_display (false);
2023                         rv->region()->playlist()->clear_owned_changes ();
2024
2025                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2026
2027                         if (arv) {
2028                                 arv->temporarily_hide_envelope ();
2029                                 arv->drag_start ();
2030                         }
2031
2032                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2033                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2034
2035                         if (insert_result.second) {
2036                                 pl->freeze();
2037                         }
2038                 }
2039         }
2040
2041         bool non_overlap_trim = false;
2042
2043         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2044                 non_overlap_trim = true;
2045         }
2046
2047         /* contstrain trim to fade length */
2048         if (_preserve_fade_anchor) {
2049                 switch (_operation) {
2050                         case StartTrim:
2051                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2052                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2053                                         if (!arv) continue;
2054                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2055                                         if (ar->locked()) continue;
2056                                         framecnt_t len = ar->fade_in()->back()->when;
2057                                         if (len < dt) dt = min(dt, len);
2058                                 }
2059                                 break;
2060                         case EndTrim:
2061                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2062                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2063                                         if (!arv) continue;
2064                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2065                                         if (ar->locked()) continue;
2066                                         framecnt_t len = ar->fade_out()->back()->when;
2067                                         if (len < -dt) dt = max(dt, -len);
2068                                 }
2069                                 break;
2070                         case ContentsTrim:
2071                                 break;
2072                 }
2073         }
2074
2075
2076         switch (_operation) {
2077         case StartTrim:
2078                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2079                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2080                         if (changed && _preserve_fade_anchor) {
2081                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2082                                 if (arv) {
2083                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2084                                         framecnt_t len = ar->fade_in()->back()->when;
2085                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2086                                         framepos_t new_length = len - diff;
2087                                         i->anchored_fade_length = min (ar->length(), new_length);
2088                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2089                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2090                                 }
2091                         }
2092                 }
2093                 break;
2094
2095         case EndTrim:
2096                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2097                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2098                         if (changed && _preserve_fade_anchor) {
2099                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2100                                 if (arv) {
2101                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2102                                         framecnt_t len = ar->fade_out()->back()->when;
2103                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2104                                         framepos_t new_length = len + diff;
2105                                         i->anchored_fade_length = min (ar->length(), new_length);
2106                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2107                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2108                                 }
2109                         }
2110                 }
2111                 break;
2112
2113         case ContentsTrim:
2114                 {
2115                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2116
2117                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2118                                 i->view->move_contents (frame_delta);
2119                         }
2120                 }
2121                 break;
2122         }
2123
2124         switch (_operation) {
2125         case StartTrim:
2126                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2127                 break;
2128         case EndTrim:
2129                 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2130                 break;
2131         case ContentsTrim:
2132                 // show_verbose_cursor_time (frame_delta);
2133                 break;
2134         }
2135 }
2136
2137
2138 void
2139 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2140 {
2141         if (movement_occurred) {
2142                 motion (event, false);
2143
2144                 if (_operation == StartTrim) {
2145                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2146                                 {
2147                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2148                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2149                                            needs to be zero in the undo record.
2150                                         */
2151                                         i->view->trim_front_ending ();
2152                                 }
2153                                 if (_preserve_fade_anchor) {
2154                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2155                                         if (arv) {
2156                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2157                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2158                                                 ar->set_fade_in_length(i->anchored_fade_length);
2159                                                 ar->set_fade_in_active(true);
2160                                         }
2161                                 }
2162                                 if (_jump_position_when_done) {
2163                                         i->view->region()->set_position (i->initial_position);
2164                                 }
2165                         }
2166                 } else if (_operation == EndTrim) {
2167                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2168                                 if (_preserve_fade_anchor) {
2169                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2170                                         if (arv) {
2171                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2172                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2173                                                 ar->set_fade_out_length(i->anchored_fade_length);
2174                                                 ar->set_fade_out_active(true);
2175                                         }
2176                                 }
2177                                 if (_jump_position_when_done) {
2178                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
2179                                 }
2180                         }
2181                 }
2182
2183                 if (!_views.empty()) {
2184                         if (_operation == StartTrim) {
2185                                 _editor->maybe_locate_with_edit_preroll(
2186                                         _views.begin()->view->region()->position());
2187                         }
2188                         if (_operation == EndTrim) {
2189                                 _editor->maybe_locate_with_edit_preroll(
2190                                         _views.begin()->view->region()->position() +
2191                                         _views.begin()->view->region()->length());
2192                         }
2193                 }
2194         
2195                 if (!_editor->selection->selected (_primary)) {
2196                         _primary->thaw_after_trim ();
2197                 } else {
2198
2199                         set<boost::shared_ptr<Playlist> > diffed_playlists;
2200
2201                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2202                                 i->view->thaw_after_trim ();
2203                                 i->view->enable_display (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                 ArdourCanvas::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, TimeAxisView* itav)
4850         : view (v)
4851         , initial_time_axis_view (itav)
4852 {
4853         /* note that time_axis_view may be null if the regionview was created
4854          * as part of a copy operation.
4855          */
4856         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4857         layer = v->region()->layer ();
4858         initial_y = v->get_canvas_group()->position().y;
4859         initial_playlist = v->region()->playlist ();
4860         initial_position = v->region()->position ();
4861         initial_end = v->region()->position () + v->region()->length ();
4862 }
4863
4864 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4865         : Drag (e, i->canvas_item ())
4866         , _region_view (r)
4867         , _patch_change (i)
4868         , _cumulative_dx (0)
4869 {
4870         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4871                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4872                                                    grab_frame()));
4873 }
4874
4875 void
4876 PatchChangeDrag::motion (GdkEvent* ev, bool)
4877 {
4878         framepos_t f = adjusted_current_frame (ev);
4879         boost::shared_ptr<Region> r = _region_view->region ();
4880         f = max (f, r->position ());
4881         f = min (f, r->last_frame ());
4882
4883         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4884         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4885         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4886         _cumulative_dx = dxu;
4887 }
4888
4889 void
4890 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4891 {
4892         if (!movement_occurred) {
4893                 return;
4894         }
4895
4896         boost::shared_ptr<Region> r (_region_view->region ());
4897         framepos_t f = adjusted_current_frame (ev);
4898         f = max (f, r->position ());
4899         f = min (f, r->last_frame ());
4900
4901         _region_view->move_patch_change (
4902                 *_patch_change,
4903                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4904                 );
4905 }
4906
4907 void
4908 PatchChangeDrag::aborted (bool)
4909 {
4910         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4911 }
4912
4913 void
4914 PatchChangeDrag::setup_pointer_frame_offset ()
4915 {
4916         boost::shared_ptr<Region> region = _region_view->region ();
4917         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4918 }
4919
4920 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4921         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4922         , _region_view (rv)
4923 {
4924
4925 }
4926
4927 void
4928 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4929 {
4930         framepos_t const p = _region_view->region()->position ();
4931         double const y = _region_view->midi_view()->y_position ();
4932
4933         x1 = max ((framepos_t) 0, x1 - p);
4934         x2 = max ((framepos_t) 0, x2 - p);
4935         y1 = max (0.0, y1 - y);
4936         y2 = max (0.0, y2 - y);
4937         
4938         _region_view->update_drag_selection (
4939                 _editor->sample_to_pixel (x1),
4940                 _editor->sample_to_pixel (x2),
4941                 y1,
4942                 y2,
4943                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4944                 );
4945 }
4946
4947 void
4948 MidiRubberbandSelectDrag::deselect_things ()
4949 {
4950         /* XXX */
4951 }
4952
4953 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4954         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4955         , _region_view (rv)
4956 {
4957         _vertical_only = true;
4958 }
4959
4960 void
4961 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4962 {
4963         double const y = _region_view->midi_view()->y_position ();
4964
4965         y1 = max (0.0, y1 - y);
4966         y2 = max (0.0, y2 - y);
4967         
4968         _region_view->update_vertical_drag_selection (
4969                 y1,
4970                 y2,
4971                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4972                 );
4973 }
4974
4975 void
4976 MidiVerticalSelectDrag::deselect_things ()
4977 {
4978         /* XXX */
4979 }
4980
4981 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4982         : RubberbandSelectDrag (e, i)
4983 {
4984
4985 }
4986
4987 void
4988 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4989 {
4990         if (drag_in_progress) {
4991                 /* We just want to select things at the end of the drag, not during it */
4992                 return;
4993         }
4994         
4995         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4996         
4997         _editor->begin_reversible_command (_("rubberband selection"));
4998         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4999         _editor->commit_reversible_command ();
5000 }
5001
5002 void
5003 EditorRubberbandSelectDrag::deselect_things ()
5004 {
5005         if (!getenv("ARDOUR_SAE")) {
5006                 _editor->selection->clear_tracks();
5007         }
5008         _editor->selection->clear_regions();
5009         _editor->selection->clear_points ();
5010         _editor->selection->clear_lines ();
5011 }
5012
5013 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5014         : Drag (e, i)
5015         , _region_view (rv)
5016         , _drag_rect (0)
5017 {
5018         
5019 }
5020
5021 NoteCreateDrag::~NoteCreateDrag ()
5022 {
5023         delete _drag_rect;
5024 }
5025
5026 framecnt_t
5027 NoteCreateDrag::grid_frames (framepos_t t) const
5028 {
5029         bool success;
5030         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5031         if (!success) {
5032                 grid_beats = 1;
5033         }
5034
5035         return _region_view->region_beats_to_region_frames (grid_beats);
5036 }
5037
5038 void
5039 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5040 {
5041         Drag::start_grab (event, cursor);
5042                                                  
5043         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5044
5045         framepos_t pf = _drags->current_pointer_frame ();
5046         framecnt_t const g = grid_frames (pf);
5047
5048         /* Hack so that we always snap to the note that we are over, instead of snapping
5049            to the next one if we're more than halfway through the one we're over.
5050         */
5051         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5052                 pf -= g / 2;
5053         }
5054
5055         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5056
5057         MidiStreamView* sv = _region_view->midi_stream_view ();
5058         double const x = _editor->sample_to_pixel (_note[0]);
5059         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5060
5061         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5062         _drag_rect->set_outline_all ();
5063         _drag_rect->set_outline_color (0xffffff99);
5064         _drag_rect->set_fill_color (0xffffff66);
5065 }
5066
5067 void
5068 NoteCreateDrag::motion (GdkEvent* event, bool)
5069 {
5070         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5071         double const x = _editor->sample_to_pixel (_note[1]);
5072         if (_note[1] > _note[0]) {
5073                 _drag_rect->set_x1 (x);
5074         } else {
5075                 _drag_rect->set_x0 (x);
5076         }
5077 }
5078
5079 void
5080 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5081 {
5082         if (!had_movement) {
5083                 return;
5084         }
5085         
5086         framepos_t const start = min (_note[0], _note[1]);
5087         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5088
5089         framecnt_t const g = grid_frames (start);
5090         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5091         
5092         if (_editor->snap_mode() == SnapNormal && length < g) {
5093                 length = g - one_tick;
5094         }
5095
5096         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5097
5098         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5099 }
5100
5101 double
5102 NoteCreateDrag::y_to_region (double y) const
5103 {
5104         double x = 0;
5105         _region_view->get_canvas_group()->canvas_to_item (x, y);
5106         return y;
5107 }
5108
5109 void
5110 NoteCreateDrag::aborted (bool)
5111 {
5112         
5113 }
5114
5115 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5116         : Drag (e, i)
5117         , arv (rv)
5118         , start (start_yn)
5119 {
5120         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5121 }
5122
5123 void
5124 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5125 {
5126         Drag::start_grab (event, cursor);
5127 }
5128
5129 void
5130 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5131 {
5132         double distance;
5133         double new_length;
5134         framecnt_t len;
5135
5136         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5137
5138         if (start) {
5139                 distance = _drags->current_pointer_x() - grab_x();
5140                 len = ar->fade_in()->back()->when;
5141         } else {
5142                 distance = grab_x() - _drags->current_pointer_x();
5143                 len = ar->fade_out()->back()->when;
5144         }
5145
5146         /* how long should it be ? */
5147
5148         new_length = len + _editor->pixel_to_sample (distance);
5149
5150         /* now check with the region that this is legal */
5151
5152         new_length = ar->verify_xfade_bounds (new_length, start);
5153
5154         if (start) {
5155                 arv->reset_fade_in_shape_width (ar, new_length);
5156         } else {
5157                 arv->reset_fade_out_shape_width (ar, new_length);
5158         }
5159 }
5160
5161 void
5162 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5163 {
5164         double distance;
5165         double new_length;
5166         framecnt_t len;
5167
5168         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5169
5170         if (start) {
5171                 distance = _drags->current_pointer_x() - grab_x();
5172                 len = ar->fade_in()->back()->when;
5173         } else {
5174                 distance = grab_x() - _drags->current_pointer_x();
5175                 len = ar->fade_out()->back()->when;
5176         }
5177
5178         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5179         
5180         _editor->begin_reversible_command ("xfade trim");
5181         ar->playlist()->clear_owned_changes (); 
5182
5183         if (start) {
5184                 ar->set_fade_in_length (new_length);
5185         } else {
5186                 ar->set_fade_out_length (new_length);
5187         }
5188
5189         /* Adjusting the xfade may affect other regions in the playlist, so we need
5190            to get undo Commands from the whole playlist rather than just the
5191            region.
5192         */
5193
5194         vector<Command*> cmds;
5195         ar->playlist()->rdiff (cmds);
5196         _editor->session()->add_commands (cmds);
5197         _editor->commit_reversible_command ();
5198
5199 }
5200
5201 void
5202 CrossfadeEdgeDrag::aborted (bool)
5203 {
5204         if (start) {
5205                 arv->redraw_start_xfade ();
5206         } else {
5207                 arv->redraw_end_xfade ();
5208         }
5209 }
5210