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