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