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