'gtk2_ardour' - Add namespaces + casting where necessary + general bits of 'correctne...
[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
1285                 StreamView* sview = (*i)->view();
1286
1287                 if (sview) {
1288                         if (sview->layer_display() == Expanded) {
1289                                 sview->set_layer_display (Stacked);
1290                         }
1291                 }
1292         }
1293         
1294         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1295                 RegionView* rv = i->view;
1296                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1297                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1298                 assert (rtv);
1299                 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1300                 rv->get_canvas_group()->set_y_position (0);
1301                 rv->drag_end ();
1302                 rv->fake_set_opaque (false);
1303                 rv->move (-_total_x_delta, 0);
1304                 rv->set_height (rtv->view()->child_height ());
1305         }
1306 }
1307
1308 /** @param b true to brush, otherwise false.
1309  *  @param c true to make copies of the regions being moved, otherwise false.
1310  */
1311 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1312         : RegionMotionDrag (e, i, p, v, b),
1313           _copy (c)
1314 {
1315         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1316
1317         double speed = 1;
1318         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1319         if (rtv && rtv->is_track()) {
1320                 speed = rtv->track()->speed ();
1321         }
1322
1323         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1324 }
1325
1326 void
1327 RegionMoveDrag::setup_pointer_frame_offset ()
1328 {
1329         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1330 }
1331
1332 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1333         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1334 {
1335         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1336
1337         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1338                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1339
1340         _primary = v->view()->create_region_view (r, false, false);
1341
1342         _primary->get_canvas_group()->show ();
1343         _primary->set_position (pos, 0);
1344         _views.push_back (DraggingView (_primary, this));
1345
1346         _last_frame_position = pos;
1347
1348         _item = _primary->get_canvas_group ();
1349 }
1350
1351 void
1352 RegionInsertDrag::finished (GdkEvent *, bool)
1353 {
1354         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1355
1356         _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1357         _primary->get_canvas_group()->set_y_position (0);
1358
1359         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1360
1361         _editor->begin_reversible_command (Operations::insert_region);
1362         playlist->clear_changes ();
1363         playlist->add_region (_primary->region (), _last_frame_position);
1364         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1365         _editor->commit_reversible_command ();
1366
1367         delete _primary;
1368         _primary = 0;
1369         _views.clear ();
1370 }
1371
1372 void
1373 RegionInsertDrag::aborted (bool)
1374 {
1375         delete _primary;
1376         _primary = 0;
1377         _views.clear ();
1378 }
1379
1380 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1381         : RegionMoveDrag (e, i, p, v, false, false)
1382 {
1383         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1384 }
1385
1386 struct RegionSelectionByPosition {
1387     bool operator() (RegionView*a, RegionView* b) {
1388             return a->region()->position () < b->region()->position();
1389     }
1390 };
1391
1392 void
1393 RegionSpliceDrag::motion (GdkEvent* event, bool)
1394 {
1395         /* Which trackview is this ? */
1396
1397         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1398         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1399
1400         /* The region motion is only processed if the pointer is over
1401            an audio track.
1402         */
1403
1404         if (!tv || !tv->is_track()) {
1405                 /* To make sure we hide the verbose canvas cursor when the mouse is
1406                    not held over and audiotrack.
1407                 */
1408                 _editor->verbose_cursor()->hide ();
1409                 return;
1410         }
1411
1412         int dir;
1413
1414         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1415                 dir = 1;
1416         } else {
1417                 dir = -1;
1418         }
1419
1420         RegionSelection copy (_editor->selection->regions);
1421
1422         RegionSelectionByPosition cmp;
1423         copy.sort (cmp);
1424
1425         framepos_t const pf = adjusted_current_frame (event);
1426
1427         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1428
1429                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1430
1431                 if (!atv) {
1432                         continue;
1433                 }
1434
1435                 boost::shared_ptr<Playlist> playlist;
1436
1437                 if ((playlist = atv->playlist()) == 0) {
1438                         continue;
1439                 }
1440
1441                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1442                         continue;
1443                 }
1444
1445                 if (dir > 0) {
1446                         if (pf < (*i)->region()->last_frame() + 1) {
1447                                 continue;
1448                         }
1449                 } else {
1450                         if (pf > (*i)->region()->first_frame()) {
1451                                 continue;
1452                         }
1453                 }
1454
1455
1456                 playlist->shuffle ((*i)->region(), dir);
1457         }
1458 }
1459
1460 void
1461 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1462 {
1463         RegionMoveDrag::finished (event, movement_occurred);
1464 }
1465
1466 void
1467 RegionSpliceDrag::aborted (bool)
1468 {
1469         /* XXX: TODO */
1470 }
1471
1472 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1473         : Drag (e, i),
1474           _view (dynamic_cast<MidiTimeAxisView*> (v))
1475 {
1476         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1477
1478         assert (_view);
1479 }
1480
1481 void
1482 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1483 {
1484         if (first_move) {
1485                 _region = add_midi_region (_view);
1486                 _view->playlist()->freeze ();
1487         } else {
1488                 if (_region) {
1489                         framepos_t const f = adjusted_current_frame (event);
1490                         if (f < grab_frame()) {
1491                                 _region->set_position (f);
1492                         }
1493
1494                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1495                            so that if this region is duplicated, its duplicate starts on
1496                            a snap point rather than 1 frame after a snap point.  Otherwise things get
1497                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
1498                            place snapped notes at the start of the region.
1499                         */
1500
1501                         framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1502                         _region->set_length (len < 1 ? 1 : len);
1503                 }
1504         }
1505 }
1506
1507 void
1508 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1509 {
1510         if (!movement_occurred) {
1511                 add_midi_region (_view);
1512         } else {
1513                 _view->playlist()->thaw ();
1514         }
1515 }
1516
1517 void
1518 RegionCreateDrag::aborted (bool)
1519 {
1520         if (_region) {
1521                 _view->playlist()->thaw ();
1522         }
1523
1524         /* XXX */
1525 }
1526
1527 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1528         : Drag (e, i)
1529         , region (0)
1530 {
1531         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1532 }
1533
1534 void
1535 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1536 {
1537         Gdk::Cursor* cursor;
1538         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1539         assert (cnote);
1540         float x_fraction = cnote->mouse_x_fraction ();
1541
1542         if (x_fraction > 0.0 && x_fraction < 0.25) {
1543                 cursor = _editor->cursors()->left_side_trim;
1544         } else  {
1545                 cursor = _editor->cursors()->right_side_trim;
1546         }
1547
1548         Drag::start_grab (event, cursor);
1549
1550         region = &cnote->region_view();
1551
1552         double const region_start = region->get_position_pixels();
1553         double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1554
1555         if (grab_x() <= middle_point) {
1556                 cursor = _editor->cursors()->left_side_trim;
1557                 at_front = true;
1558         } else {
1559                 cursor = _editor->cursors()->right_side_trim;
1560                 at_front = false;
1561         }
1562
1563         _item->grab ();
1564
1565         if (event->motion.state & Keyboard::PrimaryModifier) {
1566                 relative = false;
1567         } else {
1568                 relative = true;
1569         }
1570
1571         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1572
1573         if (ms.size() > 1) {
1574                 /* has to be relative, may make no sense otherwise */
1575                 relative = true;
1576         }
1577
1578         /* select this note; if it is already selected, preserve the existing selection,
1579            otherwise make this note the only one selected.
1580         */
1581         region->note_selected (cnote, cnote->selected ());
1582
1583         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1584                 MidiRegionSelection::iterator next;
1585                 next = r;
1586                 ++next;
1587                 (*r)->begin_resizing (at_front);
1588                 r = next;
1589         }
1590 }
1591
1592 void
1593 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1594 {
1595         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1596         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1597                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1598                 assert (nb);
1599                 (*r)->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1600         }
1601 }
1602
1603 void
1604 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1605 {
1606         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1607         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1608                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1609                 assert (nb);
1610                 (*r)->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1611         }
1612 }
1613
1614 void
1615 NoteResizeDrag::aborted (bool)
1616 {
1617         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1618         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1619                 (*r)->abort_resizing ();
1620         }
1621 }
1622
1623 AVDraggingView::AVDraggingView (RegionView* v)
1624         : view (v)
1625 {
1626         initial_position = v->region()->position ();
1627 }
1628
1629 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
1630         : Drag (e, i)
1631 {
1632         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
1633
1634         RegionSelection rs;
1635         TrackViewList empty;
1636         empty.clear();
1637         _editor->get_regions_after(rs, (framepos_t) 0, empty);
1638         std::list<RegionView*> views = rs.by_layer();
1639
1640         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
1641                 RegionView* rv = (*i);
1642                 if (!rv->region()->video_locked()) {
1643                         continue;
1644                 }
1645                 _views.push_back (AVDraggingView (rv));
1646         }
1647 }
1648
1649 void
1650 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1651 {
1652         Drag::start_grab (event);
1653         if (_editor->session() == 0) {
1654                 return;
1655         }
1656
1657         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
1658         _max_backwards_drag = (
1659                           ARDOUR_UI::instance()->video_timeline->get_duration()
1660                         + ARDOUR_UI::instance()->video_timeline->get_offset()
1661                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
1662                         );
1663
1664         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1665                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
1666                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
1667                 }
1668         }
1669         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
1670
1671         char buf[128];
1672         Timecode::Time timecode;
1673         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
1674         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);
1675         _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1676         _editor->verbose_cursor()->show ();
1677 }
1678
1679 void
1680 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
1681 {
1682         if (_editor->session() == 0) {
1683                 return;
1684         }
1685         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1686                 return;
1687         }
1688
1689         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
1690         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
1691
1692         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
1693                 dt = - _max_backwards_drag;
1694         }
1695
1696         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
1697         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1698
1699         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1700                 RegionView* rv = i->view;
1701                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
1702                 if (first_move) {
1703                         rv->drag_start ();
1704                         rv->fake_set_opaque (true);
1705                         rv->region()->clear_changes ();
1706                         rv->region()->suspend_property_changes();
1707                 }
1708                 rv->region()->set_position(i->initial_position + dt);
1709                 rv->region_changed(ARDOUR::Properties::position);
1710         }
1711
1712         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
1713         Timecode::Time timecode;
1714         Timecode::Time timediff;
1715         char buf[128];
1716         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
1717         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
1718         snprintf (buf, sizeof (buf),
1719                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1720                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
1721                         , _("Video Start:"),
1722                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
1723                         , _("Diff:"),
1724                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
1725                                 );
1726         _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
1727         _editor->verbose_cursor()->show ();
1728 }
1729
1730 void
1731 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
1732 {
1733         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1734                 return;
1735         }
1736
1737         if (!movement_occurred || ! _editor->session()) {
1738                 return;
1739         }
1740
1741         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1742
1743         _editor->begin_reversible_command (_("Move Video"));
1744
1745         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
1746         ARDOUR_UI::instance()->video_timeline->save_undo();
1747         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
1748         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
1749
1750         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1751                 i->view->drag_end();
1752                 i->view->fake_set_opaque (false);
1753                 i->view->region()->resume_property_changes ();
1754
1755                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1756         }
1757
1758         _editor->session()->maybe_update_session_range(
1759                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
1760                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
1761                         );
1762
1763
1764         _editor->commit_reversible_command ();
1765 }
1766
1767 void
1768 VideoTimeLineDrag::aborted (bool)
1769 {
1770         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
1771                 return;
1772         }
1773         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
1774         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
1775
1776         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1777                 i->view->region()->resume_property_changes ();
1778                 i->view->region()->set_position(i->initial_position);
1779         }
1780 }
1781
1782 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
1783         : RegionDrag (e, i, p, v)
1784 {
1785         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1786         _preserve_fade_anchor = preserve_fade_anchor;
1787 }
1788
1789 void
1790 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1791 {
1792         double speed = 1.0;
1793         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1794         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1795
1796         if (tv && tv->is_track()) {
1797                 speed = tv->track()->speed();
1798         }
1799
1800         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1801         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1802         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1803
1804         framepos_t const pf = adjusted_current_frame (event);
1805
1806         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1807                 /* Move the contents of the region around without changing the region bounds */
1808                 _operation = ContentsTrim;
1809                 Drag::start_grab (event, _editor->cursors()->trimmer);
1810         } else {
1811                 /* These will get overridden for a point trim.*/
1812                 if (pf < (region_start + region_length/2)) {
1813                         /* closer to front */
1814                         _operation = StartTrim;
1815                         Drag::start_grab (event, _editor->cursors()->left_side_trim);
1816                 } else {
1817                         /* closer to end */
1818                         _operation = EndTrim;
1819                         Drag::start_grab (event, _editor->cursors()->right_side_trim);
1820                 }
1821         }
1822
1823         switch (_operation) {
1824         case StartTrim:
1825                 show_verbose_cursor_time (region_start);
1826                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1827                         i->view->trim_front_starting ();
1828                 }
1829                 break;
1830         case EndTrim:
1831                 show_verbose_cursor_time (region_end);
1832                 break;
1833         case ContentsTrim:
1834                 show_verbose_cursor_time (pf);
1835                 break;
1836         }
1837
1838         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1839                 i->view->region()->suspend_property_changes ();
1840         }
1841 }
1842
1843 void
1844 TrimDrag::motion (GdkEvent* event, bool first_move)
1845 {
1846         RegionView* rv = _primary;
1847
1848         double speed = 1.0;
1849         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1850         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1851         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1852         frameoffset_t frame_delta = 0;
1853
1854         if (tv && tv->is_track()) {
1855                 speed = tv->track()->speed();
1856         }
1857
1858         framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1859
1860         if (first_move) {
1861
1862                 string trim_type;
1863
1864                 switch (_operation) {
1865                 case StartTrim:
1866                         trim_type = "Region start trim";
1867                         break;
1868                 case EndTrim:
1869                         trim_type = "Region end trim";
1870                         break;
1871                 case ContentsTrim:
1872                         trim_type = "Region content trim";
1873                         break;
1874                 }
1875
1876                 _editor->begin_reversible_command (trim_type);
1877
1878                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1879                         RegionView* rv = i->view;
1880                         rv->fake_set_opaque (false);
1881                         rv->enable_display (false);
1882                         rv->region()->playlist()->clear_owned_changes ();
1883
1884                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1885
1886                         if (arv) {
1887                                 arv->temporarily_hide_envelope ();
1888                                 arv->drag_start ();
1889                         }
1890
1891                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1892                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1893
1894                         if (insert_result.second) {
1895                                 pl->freeze();
1896                         }
1897                 }
1898         }
1899
1900         bool non_overlap_trim = false;
1901
1902         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1903                 non_overlap_trim = true;
1904         }
1905
1906         switch (_operation) {
1907         case StartTrim:
1908                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1909                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1910                         if (changed && _preserve_fade_anchor) {
1911                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1912                                 if (arv) {
1913                                         double distance;
1914                                         double new_length;
1915                                         framecnt_t len;
1916                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1917                                         distance = _drags->current_pointer_x() - grab_x();
1918                                         len = ar->fade_in()->back()->when;
1919                                         new_length = len - _editor->pixel_to_sample (distance);
1920                                         new_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
1921                                         arv->reset_fade_in_shape_width (ar, new_length);  //the grey shape
1922                                 }
1923                         }
1924                 }
1925                 break;
1926
1927         case EndTrim:
1928                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1929                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1930                         if (changed && _preserve_fade_anchor) {
1931                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1932                                 if (arv) {
1933                                         double distance;
1934                                         double new_length;
1935                                         framecnt_t len;
1936                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1937                                         distance = grab_x() - _drags->current_pointer_x();
1938                                         len = ar->fade_out()->back()->when;
1939                                         new_length = len - _editor->pixel_to_sample (distance);
1940                                         new_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
1941                                         arv->reset_fade_out_shape_width (ar, new_length);  //the grey shape
1942                                 }
1943                         }
1944                 }
1945                 break;
1946
1947         case ContentsTrim:
1948                 {
1949                         frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1950                         // frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1951
1952                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1953                                 i->view->move_contents (frame_delta);
1954                         }
1955                 }
1956                 break;
1957         }
1958
1959         switch (_operation) {
1960         case StartTrim:
1961                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
1962                 break;
1963         case EndTrim:
1964                 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
1965                 break;
1966         case ContentsTrim:
1967                 // show_verbose_cursor_time (frame_delta);
1968                 break;
1969         }
1970 }
1971
1972
1973 void
1974 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1975 {
1976         if (movement_occurred) {
1977                 motion (event, false);
1978
1979                 if (_operation == StartTrim) {
1980                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1981                                 {
1982                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
1983                                            `correct' (ahem) the region's _start from being negative to being zero.  It
1984                                            needs to be zero in the undo record.
1985                                         */
1986                                         i->view->trim_front_ending ();
1987                                 }
1988                                 if (_preserve_fade_anchor) {
1989                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
1990                                         if (arv) {
1991                                                 double distance;
1992                                                 double new_length;
1993                                                 framecnt_t len;
1994                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
1995                                                 distance = _drags->current_pointer_x() - grab_x();
1996                                                 len = ar->fade_in()->back()->when;
1997                                                 new_length = len - _editor->pixel_to_sample (distance);
1998                                                 new_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
1999                                                 ar->set_fade_in_length(new_length);
2000                                         }
2001                                 }
2002                         }
2003                 } else if (_operation == EndTrim) {
2004                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2005                                 if (_preserve_fade_anchor) {
2006                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2007                                         if (arv) {
2008                                                 double distance;
2009                                                 double new_length;
2010                                                 framecnt_t len;
2011                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2012                                                 distance = _drags->current_pointer_x() - grab_x();
2013                                                 len = ar->fade_out()->back()->when;
2014                                                 new_length = len - _editor->pixel_to_sample (distance);
2015                                                 new_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2016                                                 ar->set_fade_out_length(new_length);
2017                                         }
2018                                 }
2019                         }
2020                 }
2021
2022                 if (!_views.empty()) {
2023                         if (_operation == StartTrim) {
2024                                 _editor->maybe_locate_with_edit_preroll(
2025                                         _views.begin()->view->region()->position());
2026                         }
2027                         if (_operation == EndTrim) {
2028                                 _editor->maybe_locate_with_edit_preroll(
2029                                         _views.begin()->view->region()->position() +
2030                                         _views.begin()->view->region()->length());
2031                         }
2032                 }
2033         
2034                 if (!_editor->selection->selected (_primary)) {
2035                         _primary->thaw_after_trim ();
2036                 } else {
2037
2038                         set<boost::shared_ptr<Playlist> > diffed_playlists;
2039
2040                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2041                                 i->view->thaw_after_trim ();
2042                                 i->view->enable_display (true);
2043                                 i->view->fake_set_opaque (true);
2044
2045                                 /* Trimming one region may affect others on the playlist, so we need
2046                                    to get undo Commands from the whole playlist rather than just the
2047                                    region.  Use diffed_playlists to make sure we don't diff a given
2048                                    playlist more than once.
2049                                 */
2050                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2051                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2052                                         vector<Command*> cmds;
2053                                         p->rdiff (cmds);
2054                                         _editor->session()->add_commands (cmds);
2055                                         diffed_playlists.insert (p);
2056                                 }
2057                         }
2058                 }
2059
2060                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2061                         (*p)->thaw ();
2062                 }
2063
2064                 _editor->motion_frozen_playlists.clear ();
2065                 _editor->commit_reversible_command();
2066
2067         } else {
2068                 /* no mouse movement */
2069                 _editor->point_trim (event, adjusted_current_frame (event));
2070         }
2071
2072         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2073                 if (_operation == StartTrim) {
2074                         i->view->trim_front_ending ();
2075                 }
2076
2077                 i->view->region()->resume_property_changes ();
2078         }
2079 }
2080
2081 void
2082 TrimDrag::aborted (bool movement_occurred)
2083 {
2084         /* Our motion method is changing model state, so use the Undo system
2085            to cancel.  Perhaps not ideal, as this will leave an Undo point
2086            behind which may be slightly odd from the user's point of view.
2087         */
2088
2089         finished (0, true);
2090
2091         if (movement_occurred) {
2092                 _editor->undo ();
2093         }
2094
2095         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2096                 i->view->region()->resume_property_changes ();
2097         }
2098 }
2099
2100 void
2101 TrimDrag::setup_pointer_frame_offset ()
2102 {
2103         list<DraggingView>::iterator i = _views.begin ();
2104         while (i != _views.end() && i->view != _primary) {
2105                 ++i;
2106         }
2107
2108         if (i == _views.end()) {
2109                 return;
2110         }
2111
2112         switch (_operation) {
2113         case StartTrim:
2114                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2115                 break;
2116         case EndTrim:
2117                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2118                 break;
2119         case ContentsTrim:
2120                 break;
2121         }
2122 }
2123
2124 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2125         : Drag (e, i),
2126           _copy (c)
2127 {
2128         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2129         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2130         assert (_marker);
2131 }
2132
2133 void
2134 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2135 {
2136         Drag::start_grab (event, cursor);
2137         show_verbose_cursor_time (adjusted_current_frame(event));
2138 }
2139
2140 void
2141 MeterMarkerDrag::setup_pointer_frame_offset ()
2142 {
2143         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2144 }
2145
2146 void
2147 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2148 {
2149         if (first_move) {
2150
2151                 // create a dummy marker for visual representation of moving the
2152                 // section, because whether its a copy or not, we're going to 
2153                 // leave or lose the original marker (leave if its a copy; lose if its
2154                 // not, because we'll remove it from the map).
2155                 
2156                 MeterSection section (_marker->meter());
2157
2158                 if (!section.movable()) {
2159                         return;
2160                 }
2161                 
2162                 char name[64];
2163                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2164                 
2165                 _marker = new MeterMarker (
2166                         *_editor,
2167                         *_editor->meter_group,
2168                         ARDOUR_UI::config()->get_canvasvar_MeterMarker(),
2169                         name,
2170                         *new MeterSection (_marker->meter())
2171                 );
2172                 
2173                 /* use the new marker for the grab */
2174                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2175
2176                 if (!_copy) {
2177                         TempoMap& map (_editor->session()->tempo_map());
2178                         /* get current state */
2179                         before_state = &map.get_state();
2180                         /* remove the section while we drag it */
2181                         map.remove_meter (section, true);
2182                 }
2183         }
2184
2185         framepos_t const pf = adjusted_current_frame (event);
2186         _marker->set_position (pf);
2187         show_verbose_cursor_time (pf);
2188 }
2189
2190 void
2191 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2192 {
2193         if (!movement_occurred) {
2194                 return;
2195         }
2196
2197         motion (event, false);
2198
2199         Timecode::BBT_Time when;
2200
2201         TempoMap& map (_editor->session()->tempo_map());
2202         map.bbt_time (last_pointer_frame(), when);
2203         
2204         if (_copy == true) {
2205                 _editor->begin_reversible_command (_("copy meter mark"));
2206                 XMLNode &before = map.get_state();
2207                 map.add_meter (_marker->meter(), when);
2208                 XMLNode &after = map.get_state();
2209                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2210                 _editor->commit_reversible_command ();
2211
2212         } else {
2213                 _editor->begin_reversible_command (_("move meter mark"));
2214
2215                 /* we removed it before, so add it back now */
2216                 
2217                 map.add_meter (_marker->meter(), when);
2218                 XMLNode &after = map.get_state();
2219                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2220                 _editor->commit_reversible_command ();
2221         }
2222
2223         // delete the dummy marker we used for visual representation while moving.
2224         // a new visual marker will show up automatically.
2225         delete _marker;
2226 }
2227
2228 void
2229 MeterMarkerDrag::aborted (bool moved)
2230 {
2231         _marker->set_position (_marker->meter().frame ());
2232
2233         if (moved) {
2234                 TempoMap& map (_editor->session()->tempo_map());
2235                 /* we removed it before, so add it back now */
2236                 map.add_meter (_marker->meter(), _marker->meter().frame());
2237                 // delete the dummy marker we used for visual representation while moving.
2238                 // a new visual marker will show up automatically.
2239                 delete _marker;
2240         }
2241 }
2242
2243 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2244         : Drag (e, i),
2245           _copy (c)
2246 {
2247         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2248
2249         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2250         assert (_marker);
2251 }
2252
2253 void
2254 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2255 {
2256         Drag::start_grab (event, cursor);
2257         show_verbose_cursor_time (adjusted_current_frame (event));
2258 }
2259
2260 void
2261 TempoMarkerDrag::setup_pointer_frame_offset ()
2262 {
2263         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2264 }
2265
2266 void
2267 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2268 {
2269         if (first_move) {
2270
2271                 // create a dummy marker for visual representation of moving the
2272                 // section, because whether its a copy or not, we're going to 
2273                 // leave or lose the original marker (leave if its a copy; lose if its
2274                 // not, because we'll remove it from the map).
2275                 
2276                 // create a dummy marker for visual representation of moving the copy.
2277                 // The actual copying is not done before we reach the finish callback.
2278
2279                 char name[64];
2280                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2281
2282                 TempoSection section (_marker->tempo());
2283
2284                 _marker = new TempoMarker (
2285                         *_editor,
2286                         *_editor->tempo_group,
2287                         ARDOUR_UI::config()->get_canvasvar_TempoMarker(),
2288                         name,
2289                         *new TempoSection (_marker->tempo())
2290                         );
2291
2292                 /* use the new marker for the grab */
2293                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2294
2295                 if (!_copy) {
2296                         TempoMap& map (_editor->session()->tempo_map());
2297                         /* get current state */
2298                         before_state = &map.get_state();
2299                         /* remove the section while we drag it */
2300                         map.remove_tempo (section, true);
2301                 }
2302         }
2303
2304         framepos_t const pf = adjusted_current_frame (event);
2305         _marker->set_position (pf);
2306         show_verbose_cursor_time (pf);
2307 }
2308
2309 void
2310 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2311 {
2312         if (!movement_occurred) {
2313                 return;
2314         }
2315
2316         motion (event, false);
2317
2318         TempoMap& map (_editor->session()->tempo_map());
2319         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), 0);
2320         Timecode::BBT_Time when;
2321
2322         map.bbt_time (beat_time, when);
2323
2324         if (_copy == true) {
2325                 _editor->begin_reversible_command (_("copy tempo mark"));
2326                 XMLNode &before = map.get_state();
2327                 map.add_tempo (_marker->tempo(), when);
2328                 XMLNode &after = map.get_state();
2329                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2330                 _editor->commit_reversible_command ();
2331
2332         } else {
2333                 _editor->begin_reversible_command (_("move tempo mark"));
2334                 /* we removed it before, so add it back now */
2335                 map.add_tempo (_marker->tempo(), when);
2336                 XMLNode &after = map.get_state();
2337                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2338                 _editor->commit_reversible_command ();
2339         }
2340
2341         // delete the dummy marker we used for visual representation while moving.
2342         // a new visual marker will show up automatically.
2343         delete _marker;
2344 }
2345
2346 void
2347 TempoMarkerDrag::aborted (bool moved)
2348 {
2349         _marker->set_position (_marker->tempo().frame());
2350         if (moved) {
2351                 TempoMap& map (_editor->session()->tempo_map());
2352                 /* we removed it before, so add it back now */
2353                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2354                 // delete the dummy marker we used for visual representation while moving.
2355                 // a new visual marker will show up automatically.
2356                 delete _marker;
2357         }
2358 }
2359
2360 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2361         : Drag (e, &c.time_bar_canvas_item())
2362         , _cursor (c)
2363         , _stop (s)
2364 {
2365         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2366 }
2367
2368 /** Do all the things we do when dragging the playhead to make it look as though
2369  *  we have located, without actually doing the locate (because that would cause
2370  *  the diskstream buffers to be refilled, which is too slow).
2371  */
2372 void
2373 CursorDrag::fake_locate (framepos_t t)
2374 {
2375         _editor->playhead_cursor->set_position (t);
2376
2377         Session* s = _editor->session ();
2378         if (s->timecode_transmission_suspended ()) {
2379                 framepos_t const f = _editor->playhead_cursor->current_frame ();
2380                 /* This is asynchronous so it will be sent "now"
2381                  */
2382                 s->send_mmc_locate (f);
2383                 /* These are synchronous and will be sent during the next
2384                    process cycle
2385                 */
2386                 s->queue_full_time_code ();
2387                 s->queue_song_position_pointer ();
2388         }
2389
2390         show_verbose_cursor_time (t);
2391         _editor->UpdateAllTransportClocks (t);
2392 }
2393
2394 void
2395 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2396 {
2397         Drag::start_grab (event, c);
2398
2399         _grab_zoom = _editor->samples_per_pixel;
2400
2401         framepos_t where = _editor->canvas_event_frame (event);
2402
2403         _editor->snap_to_with_modifier (where, event);
2404
2405         _editor->_dragging_playhead = true;
2406
2407         Session* s = _editor->session ();
2408
2409         /* grab the track canvas item as well */
2410
2411         _cursor.track_canvas_item().grab();
2412
2413         if (s) {
2414                 if (_was_rolling && _stop) {
2415                         s->request_stop ();
2416                 }
2417
2418                 if (s->is_auditioning()) {
2419                         s->cancel_audition ();
2420                 }
2421
2422
2423                 if (AudioEngine::instance()->connected()) {
2424                         
2425                         /* do this only if we're the engine is connected
2426                          * because otherwise this request will never be
2427                          * serviced and we'll busy wait forever. likewise,
2428                          * notice if we are disconnected while waiting for the
2429                          * request to be serviced.
2430                          */
2431
2432                         s->request_suspend_timecode_transmission ();
2433                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2434                                 /* twiddle our thumbs */
2435                         }
2436                 }
2437         }
2438
2439         fake_locate (where);
2440 }
2441
2442 void
2443 CursorDrag::motion (GdkEvent* event, bool)
2444 {
2445         framepos_t const adjusted_frame = adjusted_current_frame (event);
2446         if (adjusted_frame != last_pointer_frame()) {
2447                 fake_locate (adjusted_frame);
2448         }
2449 }
2450
2451 void
2452 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2453 {
2454         _editor->_dragging_playhead = false;
2455
2456         _cursor.track_canvas_item().ungrab();
2457
2458         if (!movement_occurred && _stop) {
2459                 return;
2460         }
2461
2462         motion (event, false);
2463
2464         Session* s = _editor->session ();
2465         if (s) {
2466                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2467                 _editor->_pending_locate_request = true;
2468                 s->request_resume_timecode_transmission ();
2469         }
2470 }
2471
2472 void
2473 CursorDrag::aborted (bool)
2474 {
2475         _cursor.track_canvas_item().ungrab();
2476
2477         if (_editor->_dragging_playhead) {
2478                 _editor->session()->request_resume_timecode_transmission ();
2479                 _editor->_dragging_playhead = false;
2480         }
2481
2482         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2483 }
2484
2485 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2486         : RegionDrag (e, i, p, v)
2487 {
2488         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2489 }
2490
2491 void
2492 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2493 {
2494         Drag::start_grab (event, cursor);
2495
2496         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2497         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2498
2499         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2500 }
2501
2502 void
2503 FadeInDrag::setup_pointer_frame_offset ()
2504 {
2505         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2506         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2507         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2508 }
2509
2510 void
2511 FadeInDrag::motion (GdkEvent* event, bool)
2512 {
2513         framecnt_t fade_length;
2514
2515         framepos_t const pos = adjusted_current_frame (event);
2516
2517         boost::shared_ptr<Region> region = _primary->region ();
2518
2519         if (pos < (region->position() + 64)) {
2520                 fade_length = 64; // this should be a minimum defined somewhere
2521         } else if (pos > region->last_frame()) {
2522                 fade_length = region->length();
2523         } else {
2524                 fade_length = pos - region->position();
2525         }
2526
2527         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2528
2529                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2530
2531                 if (!tmp) {
2532                         continue;
2533                 }
2534
2535                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2536         }
2537
2538         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2539 }
2540
2541 void
2542 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2543 {
2544         if (!movement_occurred) {
2545                 return;
2546         }
2547
2548         framecnt_t fade_length;
2549
2550         framepos_t const pos = adjusted_current_frame (event);
2551
2552         boost::shared_ptr<Region> region = _primary->region ();
2553
2554         if (pos < (region->position() + 64)) {
2555                 fade_length = 64; // this should be a minimum defined somewhere
2556         } else if (pos > region->last_frame()) {
2557                 fade_length = region->length();
2558         } else {
2559                 fade_length = pos - region->position();
2560         }
2561
2562         _editor->begin_reversible_command (_("change fade in length"));
2563
2564         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2565
2566                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2567
2568                 if (!tmp) {
2569                         continue;
2570                 }
2571
2572                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2573                 XMLNode &before = alist->get_state();
2574
2575                 tmp->audio_region()->set_fade_in_length (fade_length);
2576                 tmp->audio_region()->set_fade_in_active (true);
2577
2578                 XMLNode &after = alist->get_state();
2579                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2580         }
2581
2582         _editor->commit_reversible_command ();
2583 }
2584
2585 void
2586 FadeInDrag::aborted (bool)
2587 {
2588         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2589                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2590
2591                 if (!tmp) {
2592                         continue;
2593                 }
2594
2595                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2596         }
2597 }
2598
2599 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2600         : RegionDrag (e, i, p, v)
2601 {
2602         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2603 }
2604
2605 void
2606 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2607 {
2608         Drag::start_grab (event, cursor);
2609
2610         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2611         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2612
2613         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2614 }
2615
2616 void
2617 FadeOutDrag::setup_pointer_frame_offset ()
2618 {
2619         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2620         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2621         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2622 }
2623
2624 void
2625 FadeOutDrag::motion (GdkEvent* event, bool)
2626 {
2627         framecnt_t fade_length;
2628
2629         framepos_t const pos = adjusted_current_frame (event);
2630
2631         boost::shared_ptr<Region> region = _primary->region ();
2632
2633         if (pos > (region->last_frame() - 64)) {
2634                 fade_length = 64; // this should really be a minimum fade defined somewhere
2635         }
2636         else if (pos < region->position()) {
2637                 fade_length = region->length();
2638         }
2639         else {
2640                 fade_length = region->last_frame() - pos;
2641         }
2642
2643         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2644
2645                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2646
2647                 if (!tmp) {
2648                         continue;
2649                 }
2650
2651                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2652         }
2653
2654         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2655 }
2656
2657 void
2658 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2659 {
2660         if (!movement_occurred) {
2661                 return;
2662         }
2663
2664         framecnt_t fade_length;
2665
2666         framepos_t const pos = adjusted_current_frame (event);
2667
2668         boost::shared_ptr<Region> region = _primary->region ();
2669
2670         if (pos > (region->last_frame() - 64)) {
2671                 fade_length = 64; // this should really be a minimum fade defined somewhere
2672         }
2673         else if (pos < region->position()) {
2674                 fade_length = region->length();
2675         }
2676         else {
2677                 fade_length = region->last_frame() - pos;
2678         }
2679
2680         _editor->begin_reversible_command (_("change fade out length"));
2681
2682         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2683
2684                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2685
2686                 if (!tmp) {
2687                         continue;
2688                 }
2689
2690                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2691                 XMLNode &before = alist->get_state();
2692
2693                 tmp->audio_region()->set_fade_out_length (fade_length);
2694                 tmp->audio_region()->set_fade_out_active (true);
2695
2696                 XMLNode &after = alist->get_state();
2697                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2698         }
2699
2700         _editor->commit_reversible_command ();
2701 }
2702
2703 void
2704 FadeOutDrag::aborted (bool)
2705 {
2706         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2707                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2708
2709                 if (!tmp) {
2710                         continue;
2711                 }
2712
2713                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2714         }
2715 }
2716
2717 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2718         : Drag (e, i)
2719 {
2720         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2721
2722         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2723         assert (_marker);
2724
2725         _points.push_back (ArdourCanvas::Duple (0, 0));
2726         _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2727 }
2728
2729 MarkerDrag::~MarkerDrag ()
2730 {
2731         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2732                 delete i->location;
2733         }
2734 }
2735
2736 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2737 {
2738         location = new Location (*l);
2739         markers.push_back (m);
2740         move_both = false;
2741 }
2742
2743 void
2744 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2745 {
2746         Drag::start_grab (event, cursor);
2747
2748         bool is_start;
2749
2750         Location *location = _editor->find_location_from_marker (_marker, is_start);
2751         _editor->_dragging_edit_point = true;
2752
2753         update_item (location);
2754
2755         // _drag_line->show();
2756         // _line->raise_to_top();
2757
2758         if (is_start) {
2759                 show_verbose_cursor_time (location->start());
2760         } else {
2761                 show_verbose_cursor_time (location->end());
2762         }
2763
2764         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2765
2766         switch (op) {
2767         case Selection::Toggle:
2768                 /* we toggle on the button release */
2769                 break;
2770         case Selection::Set:
2771                 if (!_editor->selection->selected (_marker)) {
2772                         _editor->selection->set (_marker);
2773                 }
2774                 break;
2775         case Selection::Extend:
2776         {
2777                 Locations::LocationList ll;
2778                 list<Marker*> to_add;
2779                 framepos_t s, e;
2780                 _editor->selection->markers.range (s, e);
2781                 s = min (_marker->position(), s);
2782                 e = max (_marker->position(), e);
2783                 s = min (s, e);
2784                 e = max (s, e);
2785                 if (e < max_framepos) {
2786                         ++e;
2787                 }
2788                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2789                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2790                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2791                         if (lm) {
2792                                 if (lm->start) {
2793                                         to_add.push_back (lm->start);
2794                                 }
2795                                 if (lm->end) {
2796                                         to_add.push_back (lm->end);
2797                                 }
2798                         }
2799                 }
2800                 if (!to_add.empty()) {
2801                         _editor->selection->add (to_add);
2802                 }
2803                 break;
2804         }
2805         case Selection::Add:
2806                 _editor->selection->add (_marker);
2807                 break;
2808         }
2809
2810         /* Set up copies for us to manipulate during the drag 
2811          */
2812
2813         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2814
2815                 Location* l = _editor->find_location_from_marker (*i, is_start);
2816
2817                 if (!l) {
2818                         continue;
2819                 }
2820
2821                 if (l->is_mark()) {
2822                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2823                 } else {
2824                         /* range: check that the other end of the range isn't
2825                            already there.
2826                         */
2827                         CopiedLocationInfo::iterator x;
2828                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2829                                 if (*(*x).location == *l) {
2830                                         break;
2831                                 }
2832                         }
2833                         if (x == _copied_locations.end()) {
2834                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2835                         } else {
2836                                 (*x).markers.push_back (*i);
2837                                 (*x).move_both = true;
2838                         }
2839                 }
2840                         
2841         }
2842 }
2843
2844 void
2845 MarkerDrag::setup_pointer_frame_offset ()
2846 {
2847         bool is_start;
2848         Location *location = _editor->find_location_from_marker (_marker, is_start);
2849         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2850 }
2851
2852 void
2853 MarkerDrag::motion (GdkEvent* event, bool)
2854 {
2855         framecnt_t f_delta = 0;
2856         bool is_start;
2857         bool move_both = false;
2858         Location *real_location;
2859         Location *copy_location = 0;
2860
2861         framepos_t const newframe = adjusted_current_frame (event);
2862         framepos_t next = newframe;
2863
2864         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2865                 move_both = true;
2866         }
2867
2868         CopiedLocationInfo::iterator x;
2869
2870         /* find the marker we're dragging, and compute the delta */
2871
2872         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2873
2874                 copy_location = (*x).location;
2875
2876                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2877
2878                         /* this marker is represented by this
2879                          * CopiedLocationMarkerInfo 
2880                          */
2881
2882                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2883                                 /* que pasa ?? */
2884                                 return;
2885                         }
2886
2887                         if (real_location->is_mark()) {
2888                                 f_delta = newframe - copy_location->start();
2889                         } else {
2890
2891
2892                                 switch (_marker->type()) {
2893                                 case Marker::SessionStart:
2894                                 case Marker::RangeStart:
2895                                 case Marker::LoopStart:
2896                                 case Marker::PunchIn:
2897                                         f_delta = newframe - copy_location->start();
2898                                         break;
2899
2900                                 case Marker::SessionEnd:
2901                                 case Marker::RangeEnd:
2902                                 case Marker::LoopEnd:
2903                                 case Marker::PunchOut:
2904                                         f_delta = newframe - copy_location->end();
2905                                         break;
2906                                 default:
2907                                         /* what kind of marker is this ? */
2908                                         return;
2909                                 }
2910                         }
2911
2912                         break;
2913                 }
2914         }
2915
2916         if (x == _copied_locations.end()) {
2917                 /* hmm, impossible - we didn't find the dragged marker */
2918                 return;
2919         }
2920
2921         /* now move them all */
2922
2923         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2924
2925                 copy_location = x->location;
2926
2927                 /* call this to find out if its the start or end */
2928
2929                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2930                         continue;
2931                 }
2932
2933                 if (real_location->locked()) {
2934                         continue;
2935                 }
2936
2937                 if (copy_location->is_mark()) {
2938
2939                         /* now move it */
2940
2941                         copy_location->set_start (copy_location->start() + f_delta);
2942
2943                 } else {
2944                         
2945                         framepos_t new_start = copy_location->start() + f_delta;
2946                         framepos_t new_end = copy_location->end() + f_delta;
2947                         
2948                         if (is_start) { // start-of-range marker
2949                                 
2950                                 if (move_both || (*x).move_both) {
2951                                         copy_location->set_start (new_start);
2952                                         copy_location->set_end (new_end);
2953                                 } else  if (new_start < copy_location->end()) {
2954                                         copy_location->set_start (new_start);
2955                                 } else if (newframe > 0) {
2956                                         _editor->snap_to (next, 1, true);
2957                                         copy_location->set_end (next);
2958                                         copy_location->set_start (newframe);
2959                                 }
2960
2961                         } else { // end marker
2962
2963                                 if (move_both || (*x).move_both) {
2964                                         copy_location->set_end (new_end);
2965                                         copy_location->set_start (new_start);
2966                                 } else if (new_end > copy_location->start()) {
2967                                         copy_location->set_end (new_end);
2968                                 } else if (newframe > 0) {
2969                                         _editor->snap_to (next, -1, true);
2970                                         copy_location->set_start (next);
2971                                         copy_location->set_end (newframe);
2972                                 }
2973                         }
2974                 }
2975
2976                 update_item (copy_location);
2977                 
2978                 /* now lookup the actual GUI items used to display this
2979                  * location and move them to wherever the copy of the location
2980                  * is now. This means that the logic in ARDOUR::Location is
2981                  * still enforced, even though we are not (yet) modifying 
2982                  * the real Location itself.
2983                  */
2984                 
2985                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2986
2987                 if (lm) {
2988                         lm->set_position (copy_location->start(), copy_location->end());
2989                 }
2990
2991         }
2992
2993         assert (!_copied_locations.empty());
2994
2995         show_verbose_cursor_time (newframe);
2996 }
2997
2998 void
2999 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3000 {
3001         if (!movement_occurred) {
3002                 
3003                 if (was_double_click()) {
3004                         cerr << "End of marker double click\n";
3005                 }
3006
3007                 /* just a click, do nothing but finish
3008                    off the selection process
3009                 */
3010
3011                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3012
3013                 switch (op) {
3014                 case Selection::Set:
3015                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3016                                 _editor->selection->set (_marker);
3017                         }
3018                         break;
3019
3020                 case Selection::Toggle:
3021                         /* we toggle on the button release, click only */
3022                         _editor->selection->toggle (_marker);
3023                         break;
3024
3025                 case Selection::Extend:
3026                 case Selection::Add:
3027                         break;
3028                 }
3029
3030                 return;
3031         }
3032
3033         _editor->_dragging_edit_point = false;
3034
3035         _editor->begin_reversible_command ( _("move marker") );
3036         XMLNode &before = _editor->session()->locations()->get_state();
3037
3038         MarkerSelection::iterator i;
3039         CopiedLocationInfo::iterator x;
3040         bool is_start;
3041
3042         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3043              x != _copied_locations.end() && i != _editor->selection->markers.end();
3044              ++i, ++x) {
3045
3046                 Location * location = _editor->find_location_from_marker (*i, is_start);
3047
3048                 if (location) {
3049
3050                         if (location->locked()) {
3051                                 return;
3052                         }
3053
3054                         if (location->is_mark()) {
3055                                 location->set_start (((*x).location)->start());
3056                         } else {
3057                                 location->set (((*x).location)->start(), ((*x).location)->end());
3058                         }
3059                 }
3060         }
3061
3062         XMLNode &after = _editor->session()->locations()->get_state();
3063         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3064         _editor->commit_reversible_command ();
3065 }
3066
3067 void
3068 MarkerDrag::aborted (bool)
3069 {
3070         /* XXX: TODO */
3071 }
3072
3073 void
3074 MarkerDrag::update_item (Location*)
3075 {
3076         /* noop */
3077 }
3078
3079 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3080         : Drag (e, i),
3081           _cumulative_x_drag (0),
3082           _cumulative_y_drag (0)
3083 {
3084         if (_zero_gain_fraction < 0.0) {
3085                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3086         }
3087
3088         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3089
3090         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3091         assert (_point);
3092 }
3093
3094
3095 void
3096 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3097 {
3098         Drag::start_grab (event, _editor->cursors()->fader);
3099
3100         // start the grab at the center of the control point so
3101         // the point doesn't 'jump' to the mouse after the first drag
3102         _fixed_grab_x = _point->get_x();
3103         _fixed_grab_y = _point->get_y();
3104
3105         float const fraction = 1 - (_point->get_y() / _point->line().height());
3106
3107         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3108
3109         _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3110                                         event->button.x + 10, event->button.y + 10);
3111
3112         _editor->verbose_cursor()->show ();
3113
3114         _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3115
3116         if (!_point->can_slide ()) {
3117                 _x_constrained = true;
3118         }
3119 }
3120
3121 void
3122 ControlPointDrag::motion (GdkEvent* event, bool)
3123 {
3124         double dx = _drags->current_pointer_x() - last_pointer_x();
3125         double dy = _drags->current_pointer_y() - last_pointer_y();
3126
3127         if (event->button.state & Keyboard::SecondaryModifier) {
3128                 dx *= 0.1;
3129                 dy *= 0.1;
3130         }
3131
3132         /* coordinate in pixels relative to the start of the region (for region-based automation)
3133            or track (for track-based automation) */
3134         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3135         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3136
3137         // calculate zero crossing point. back off by .01 to stay on the
3138         // positive side of zero
3139         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3140
3141         // make sure we hit zero when passing through
3142         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3143                 cy = zero_gain_y;
3144         }
3145
3146         if (_x_constrained) {
3147                 cx = _fixed_grab_x;
3148         }
3149         if (_y_constrained) {
3150                 cy = _fixed_grab_y;
3151         }
3152
3153         _cumulative_x_drag = cx - _fixed_grab_x;
3154         _cumulative_y_drag = cy - _fixed_grab_y;
3155
3156         cx = max (0.0, cx);
3157         cy = max (0.0, cy);
3158         cy = min ((double) _point->line().height(), cy);
3159
3160         framepos_t cx_frames = _editor->pixel_to_sample (cx);
3161
3162         if (!_x_constrained) {
3163                 _editor->snap_to_with_modifier (cx_frames, event);
3164         }
3165
3166         cx_frames = min (cx_frames, _point->line().maximum_time());
3167
3168         float const fraction = 1.0 - (cy / _point->line().height());
3169
3170         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3171
3172         _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3173 }
3174
3175 void
3176 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3177 {
3178         if (!movement_occurred) {
3179
3180                 /* just a click */
3181
3182                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3183                         _editor->reset_point_selection ();
3184                 }
3185
3186         } else {
3187                 motion (event, false);
3188         }
3189
3190         _point->line().end_drag (_pushing, _final_index);
3191         _editor->session()->commit_reversible_command ();
3192 }
3193
3194 void
3195 ControlPointDrag::aborted (bool)
3196 {
3197         _point->line().reset ();
3198 }
3199
3200 bool
3201 ControlPointDrag::active (Editing::MouseMode m)
3202 {
3203         if (m == Editing::MouseGain) {
3204                 /* always active in mouse gain */
3205                 return true;
3206         }
3207
3208         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3209         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3210 }
3211
3212 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3213         : Drag (e, i),
3214           _line (0),
3215           _cumulative_y_drag (0)
3216 {
3217         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3218 }
3219
3220 void
3221 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3222 {
3223         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3224         assert (_line);
3225
3226         _item = &_line->grab_item ();
3227
3228         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3229            origin, and ditto for y.
3230         */
3231
3232         double cx = event->button.x;
3233         double cy = event->button.y;
3234
3235         _line->parent_group().canvas_to_item (cx, cy);
3236
3237         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3238
3239         uint32_t before;
3240         uint32_t after;
3241
3242         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3243                 /* no adjacent points */
3244                 return;
3245         }
3246
3247         Drag::start_grab (event, _editor->cursors()->fader);
3248
3249         /* store grab start in parent frame */
3250
3251         _fixed_grab_x = cx;
3252         _fixed_grab_y = cy;
3253
3254         double fraction = 1.0 - (cy / _line->height());
3255
3256         _line->start_drag_line (before, after, fraction);
3257
3258         _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3259                                         event->button.x + 10, event->button.y + 10);
3260
3261         _editor->verbose_cursor()->show ();
3262 }
3263
3264 void
3265 LineDrag::motion (GdkEvent* event, bool)
3266 {
3267         double dy = _drags->current_pointer_y() - last_pointer_y();
3268
3269         if (event->button.state & Keyboard::SecondaryModifier) {
3270                 dy *= 0.1;
3271         }
3272
3273         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3274
3275         _cumulative_y_drag = cy - _fixed_grab_y;
3276
3277         cy = max (0.0, cy);
3278         cy = min ((double) _line->height(), cy);
3279
3280         double const fraction = 1.0 - (cy / _line->height());
3281         uint32_t ignored;
3282
3283         /* we are ignoring x position for this drag, so we can just pass in anything */
3284         _line->drag_motion (0, fraction, true, false, ignored);
3285
3286         _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3287 }
3288
3289 void
3290 LineDrag::finished (GdkEvent* event, bool)
3291 {
3292         motion (event, false);
3293         _line->end_drag (false, 0);
3294         _editor->session()->commit_reversible_command ();
3295 }
3296
3297 void
3298 LineDrag::aborted (bool)
3299 {
3300         _line->reset ();
3301 }
3302
3303 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3304         : Drag (e, i),
3305           _line (0),
3306           _cumulative_x_drag (0)
3307 {
3308         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3309 }
3310
3311 void
3312 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3313 {
3314         Drag::start_grab (event);
3315
3316         _line = reinterpret_cast<Line*> (_item);
3317         assert (_line);
3318
3319         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3320
3321         double cx = event->button.x;
3322         double cy = event->button.y;
3323
3324         _item->parent()->canvas_to_item (cx, cy);
3325
3326         /* store grab start in parent frame */
3327         _region_view_grab_x = cx;
3328
3329         _before = *(float*) _item->get_data ("position");
3330
3331         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3332
3333         _max_x = _editor->sample_to_pixel(_arv->get_duration());
3334 }
3335
3336 void
3337 FeatureLineDrag::motion (GdkEvent*, bool)
3338 {
3339         double dx = _drags->current_pointer_x() - last_pointer_x();
3340
3341         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3342
3343         _cumulative_x_drag += dx;
3344
3345         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3346
3347         if (cx > _max_x){
3348                 cx = _max_x;
3349         }
3350         else if(cx < 0){
3351                 cx = 0;
3352         }
3353
3354         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3355         assert (bbox);
3356         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3357
3358         float *pos = new float;
3359         *pos = cx;
3360
3361         _line->set_data ("position", pos);
3362
3363         _before = cx;
3364 }
3365
3366 void
3367 FeatureLineDrag::finished (GdkEvent*, bool)
3368 {
3369         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3370         _arv->update_transient(_before, _before);
3371 }
3372
3373 void
3374 FeatureLineDrag::aborted (bool)
3375 {
3376         //_line->reset ();
3377 }
3378
3379 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3380         : Drag (e, i)
3381         , _vertical_only (false)
3382 {
3383         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3384 }
3385
3386 void
3387 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3388 {
3389         Drag::start_grab (event);
3390         show_verbose_cursor_time (adjusted_current_frame (event));
3391 }
3392
3393 void
3394 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3395 {
3396         framepos_t start;
3397         framepos_t end;
3398         double y1;
3399         double y2;
3400
3401         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3402
3403         framepos_t grab = grab_frame ();
3404         if (Config->get_rubberbanding_snaps_to_grid ()) {
3405                 _editor->snap_to_with_modifier (grab, event);
3406         }
3407
3408         /* base start and end on initial click position */
3409
3410         if (pf < grab) {
3411                 start = pf;
3412                 end = grab;
3413         } else {
3414                 end = pf;
3415                 start = grab;
3416         }
3417
3418         if (_drags->current_pointer_y() < grab_y()) {
3419                 y1 = _drags->current_pointer_y();
3420                 y2 = grab_y();
3421         } else {
3422                 y2 = _drags->current_pointer_y();
3423                 y1 = grab_y();
3424         }
3425
3426
3427         if (start != end || y1 != y2) {
3428
3429                 double x1 = _editor->sample_to_pixel (start);
3430                 double x2 = _editor->sample_to_pixel (end);
3431
3432                 _editor->rubberband_rect->set_x0 (x1);
3433                 if (_vertical_only) {
3434                         /* fixed 10 pixel width */
3435                         _editor->rubberband_rect->set_x1 (x1 + 10);
3436                 } else {
3437                         _editor->rubberband_rect->set_x1 (x2);
3438                 } 
3439
3440                 _editor->rubberband_rect->set_y0 (y1);
3441                 _editor->rubberband_rect->set_y1 (y2);
3442                 
3443                 _editor->rubberband_rect->show();
3444                 _editor->rubberband_rect->raise_to_top();
3445
3446                 show_verbose_cursor_time (pf);
3447
3448                 do_select_things (event, true);
3449         }
3450 }
3451
3452 void
3453 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3454 {
3455         framepos_t x1;
3456         framepos_t x2;
3457         
3458         if (grab_frame() < last_pointer_frame()) {
3459                 x1 = grab_frame ();
3460                 x2 = last_pointer_frame ();
3461         } else {
3462                 x2 = grab_frame ();
3463                 x1 = last_pointer_frame ();
3464         }
3465
3466         double y1;
3467         double y2;
3468         
3469         if (_drags->current_pointer_y() < grab_y()) {
3470                 y1 = _drags->current_pointer_y();
3471                 y2 = grab_y();
3472         } else {
3473                 y2 = _drags->current_pointer_y();
3474                 y1 = grab_y();
3475         }
3476
3477         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3478 }
3479
3480 void
3481 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3482 {
3483         if (movement_occurred) {
3484
3485                 motion (event, false);
3486                 do_select_things (event, false);
3487
3488         } else {
3489
3490                 /* just a click */
3491
3492                 bool do_deselect = true;
3493                 MidiTimeAxisView* mtv;
3494
3495                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3496                         /* MIDI track */
3497                         if (_editor->selection->empty()) {
3498                                 /* nothing selected */
3499                                 add_midi_region (mtv);
3500                                 do_deselect = false;
3501                         }
3502                 } 
3503
3504                 /* do not deselect if Primary or Tertiary (toggle-select or
3505                  * extend-select are pressed.
3506                  */
3507
3508                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) && 
3509                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) && 
3510                     do_deselect) {
3511                         deselect_things ();
3512                 }
3513
3514         }
3515
3516         _editor->rubberband_rect->hide();
3517 }
3518
3519 void
3520 RubberbandSelectDrag::aborted (bool)
3521 {
3522         _editor->rubberband_rect->hide ();
3523 }
3524
3525 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3526         : RegionDrag (e, i, p, v)
3527 {
3528         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3529 }
3530
3531 void
3532 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3533 {
3534         Drag::start_grab (event, cursor);
3535
3536         show_verbose_cursor_time (adjusted_current_frame (event));
3537 }
3538
3539 void
3540 TimeFXDrag::motion (GdkEvent* event, bool)
3541 {
3542         RegionView* rv = _primary;
3543         StreamView* cv = rv->get_time_axis_view().view ();
3544
3545         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3546         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3547         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3548
3549         framepos_t const pf = adjusted_current_frame (event);
3550
3551         if (pf > rv->region()->position()) {
3552                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3553         }
3554
3555         show_verbose_cursor_time (pf);
3556 }
3557
3558 void
3559 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3560 {
3561         _primary->get_time_axis_view().hide_timestretch ();
3562
3563         if (!movement_occurred) {
3564                 return;
3565         }
3566
3567         if (last_pointer_frame() < _primary->region()->position()) {
3568                 /* backwards drag of the left edge - not usable */
3569                 return;
3570         }
3571
3572         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3573
3574         float percentage = (double) newlen / (double) _primary->region()->length();
3575
3576 #ifndef USE_RUBBERBAND
3577         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3578         if (_primary->region()->data_type() == DataType::AUDIO) {
3579                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3580         }
3581 #endif
3582
3583         if (!_editor->get_selection().regions.empty()) {
3584                 /* primary will already be included in the selection, and edit
3585                    group shared editing will propagate selection across
3586                    equivalent regions, so just use the current region
3587                    selection.
3588                 */
3589
3590                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3591                         error << _("An error occurred while executing time stretch operation") << endmsg;
3592                 }
3593         }
3594 }
3595
3596 void
3597 TimeFXDrag::aborted (bool)
3598 {
3599         _primary->get_time_axis_view().hide_timestretch ();
3600 }
3601
3602 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3603         : Drag (e, i)
3604 {
3605         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3606 }
3607
3608 void
3609 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3610 {
3611         Drag::start_grab (event);
3612 }
3613
3614 void
3615 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3616 {
3617         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3618 }
3619
3620 void
3621 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3622 {
3623         if (movement_occurred && _editor->session()) {
3624                 /* make sure we stop */
3625                 _editor->session()->request_transport_speed (0.0);
3626         }
3627 }
3628
3629 void
3630 ScrubDrag::aborted (bool)
3631 {
3632         /* XXX: TODO */
3633 }
3634
3635 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3636         : Drag (e, i)
3637         , _operation (o)
3638         , _add (false)
3639         , _extend (false)
3640         , _original_pointer_time_axis (-1)
3641         , _last_pointer_time_axis (-1)
3642         , _time_selection_at_start (!_editor->get_selection().time.empty())
3643 {
3644         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3645         
3646         if (_time_selection_at_start) {
3647                 start_at_start = _editor->get_selection().time.start();
3648                 end_at_start = _editor->get_selection().time.end_frame();
3649         }
3650 }
3651
3652 void
3653 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3654 {
3655         if (_editor->session() == 0) {
3656                 return;
3657         }
3658
3659         Gdk::Cursor* cursor = 0;
3660
3661         switch (_operation) {
3662         case CreateSelection:
3663                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3664                         _add = true;
3665                 } else {
3666                         _add = false;
3667                 }
3668                 cursor = _editor->cursors()->selector;
3669                 Drag::start_grab (event, cursor);
3670                 break;
3671
3672         case SelectionStartTrim:
3673                 if (_editor->clicked_axisview) {
3674                         _editor->clicked_axisview->order_selection_trims (_item, true);
3675                 }
3676                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3677                 break;
3678
3679         case SelectionEndTrim:
3680                 if (_editor->clicked_axisview) {
3681                         _editor->clicked_axisview->order_selection_trims (_item, false);
3682                 }
3683                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3684                 break;
3685
3686         case SelectionMove:
3687                 Drag::start_grab (event, cursor);
3688                 break;
3689
3690         case SelectionExtend:
3691                 Drag::start_grab (event, cursor);
3692                 break;
3693         }
3694
3695         if (_operation == SelectionMove) {
3696                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3697         } else {
3698                 show_verbose_cursor_time (adjusted_current_frame (event));
3699         }
3700
3701         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3702 }
3703
3704 void
3705 SelectionDrag::setup_pointer_frame_offset ()
3706 {
3707         switch (_operation) {
3708         case CreateSelection:
3709                 _pointer_frame_offset = 0;
3710                 break;
3711
3712         case SelectionStartTrim:
3713         case SelectionMove:
3714                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3715                 break;
3716
3717         case SelectionEndTrim:
3718                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3719                 break;
3720
3721         case SelectionExtend:
3722                 break;
3723         }
3724 }
3725
3726 void
3727 SelectionDrag::motion (GdkEvent* event, bool first_move)
3728 {
3729         framepos_t start = 0;
3730         framepos_t end = 0;
3731         framecnt_t length = 0;
3732         framecnt_t distance = 0;
3733
3734         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3735         if (pending_time_axis.first == 0) {
3736                 return;
3737         }
3738
3739         framepos_t const pending_position = adjusted_current_frame (event);
3740
3741         /* only alter selection if things have changed */
3742
3743         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3744                 return;
3745         }
3746
3747         switch (_operation) {
3748         case CreateSelection:
3749         {
3750                 framepos_t grab = grab_frame ();
3751
3752                 if (first_move) {
3753                         grab = adjusted_current_frame (event, false);
3754                         if (grab < pending_position) {
3755                                 _editor->snap_to (grab, -1);
3756                         }  else {
3757                                 _editor->snap_to (grab, 1);
3758                         }
3759                 }
3760
3761                 if (pending_position < grab) {
3762                         start = pending_position;
3763                         end = grab;
3764                 } else {
3765                         end = pending_position;
3766                         start = grab;
3767                 }
3768
3769                 /* first drag: Either add to the selection
3770                    or create a new selection
3771                 */
3772
3773                 if (first_move) {
3774
3775                         if (_add) {
3776                                 /* adding to the selection */
3777                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3778                                 //_editor->selection->add (_editor->clicked_axisview);
3779                                 _editor->clicked_selection = _editor->selection->add (start, end);
3780                                 _add = false;
3781                         } else {
3782                                 /* new selection */
3783
3784                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3785                                         //_editor->selection->set (_editor->clicked_axisview);
3786                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3787                                 }
3788
3789                                 _editor->clicked_selection = _editor->selection->set (start, end);
3790                         }
3791                 }
3792
3793                 /* select the track that we're in */
3794                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3795                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3796                         _editor->selection->add (pending_time_axis.first);
3797                         _added_time_axes.push_back (pending_time_axis.first);
3798                 }
3799
3800                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3801                    tracks that we selected in the first place.
3802                 */
3803
3804                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3805                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3806
3807                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3808                 while (i != _added_time_axes.end()) {
3809
3810                         list<TimeAxisView*>::iterator tmp = i;
3811                         ++tmp;
3812
3813                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3814                                 _editor->selection->remove (*i);
3815                                 _added_time_axes.remove (*i);
3816                         }
3817
3818                         i = tmp;
3819                 }
3820
3821         }
3822         break;
3823
3824         case SelectionStartTrim:
3825
3826                 start = _editor->selection->time[_editor->clicked_selection].start;
3827                 end = _editor->selection->time[_editor->clicked_selection].end;
3828
3829                 if (pending_position > end) {
3830                         start = end;
3831                 } else {
3832                         start = pending_position;
3833                 }
3834                 break;
3835
3836         case SelectionEndTrim:
3837
3838                 start = _editor->selection->time[_editor->clicked_selection].start;
3839                 end = _editor->selection->time[_editor->clicked_selection].end;
3840
3841                 if (pending_position < start) {
3842                         end = start;
3843                 } else {
3844                         end = pending_position;
3845                 }
3846
3847                 break;
3848                 
3849         case SelectionMove:
3850
3851                 start = _editor->selection->time[_editor->clicked_selection].start;
3852                 end = _editor->selection->time[_editor->clicked_selection].end;
3853
3854                 length = end - start;
3855                 distance = pending_position - start;
3856                 start = pending_position;
3857                 _editor->snap_to (start);
3858
3859                 end = start + length;
3860
3861                 break;
3862
3863         case SelectionExtend:
3864                 break;
3865         }
3866
3867         if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3868                 _editor->start_canvas_autoscroll (1, 0);
3869         }
3870
3871         if (start != end) {
3872                 switch (_operation) {
3873                 case SelectionMove:     
3874                         if (_time_selection_at_start) {
3875                                 _editor->selection->move_time (distance);
3876                         }
3877                         break;
3878                 default:
3879                         _editor->selection->replace (_editor->clicked_selection, start, end);
3880                 }
3881         }
3882
3883         if (_operation == SelectionMove) {
3884                 show_verbose_cursor_time(start);
3885         } else {
3886                 show_verbose_cursor_time(pending_position);
3887         }
3888 }
3889
3890 void
3891 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3892 {
3893         Session* s = _editor->session();
3894
3895         if (movement_occurred) {
3896                 motion (event, false);
3897                 /* XXX this is not object-oriented programming at all. ick */
3898                 if (_editor->selection->time.consolidate()) {
3899                         _editor->selection->TimeChanged ();
3900                 }
3901
3902                 /* XXX what if its a music time selection? */
3903                 if (s) {
3904                         if ( s->get_play_range() && s->transport_rolling() ) {
3905                                 s->request_play_range (&_editor->selection->time, true);
3906                         } else {
3907                                 if (Config->get_always_play_range() && !s->transport_rolling()) {
3908                                         s->request_locate (_editor->get_selection().time.start());
3909                                 }
3910                         }
3911                 }
3912
3913         } else {
3914                 /* just a click, no pointer movement.
3915                  */
3916
3917                 if (_operation == SelectionExtend) {
3918                         if (_time_selection_at_start) {
3919                                 framepos_t pos = adjusted_current_frame (event, false);
3920                                 framepos_t start = min (pos, start_at_start);
3921                                 framepos_t end = max (pos, end_at_start);
3922                                 _editor->selection->set (start, end);
3923                         }
3924                 } else {
3925                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3926                                 if (_editor->clicked_selection) {
3927                                         _editor->selection->remove (_editor->clicked_selection);
3928                                 }
3929                         } else {
3930                                 if (!_editor->clicked_selection) {
3931                                         _editor->selection->clear_time();
3932                                 }
3933                         }
3934                 }
3935
3936                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3937                         _editor->selection->set (_editor->clicked_axisview);
3938                 }
3939                         
3940                 if (s && s->get_play_range () && s->transport_rolling()) {
3941                         s->request_stop (false, false);
3942                 }
3943
3944         }
3945
3946         _editor->stop_canvas_autoscroll ();
3947         _editor->clicked_selection = 0;
3948 }
3949
3950 void
3951 SelectionDrag::aborted (bool)
3952 {
3953         /* XXX: TODO */
3954 }
3955
3956 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3957         : Drag (e, i),
3958           _operation (o),
3959           _copy (false)
3960 {
3961         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3962
3963         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group, 
3964                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
3965                                                                       physical_screen_height (_editor->get_window())));
3966         _drag_rect->hide ();
3967
3968         _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3969         _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3970 }
3971
3972 void
3973 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3974 {
3975         if (_editor->session() == 0) {
3976                 return;
3977         }
3978
3979         Gdk::Cursor* cursor = 0;
3980
3981         if (!_editor->temp_location) {
3982                 _editor->temp_location = new Location (*_editor->session());
3983         }
3984
3985         switch (_operation) {
3986         case CreateRangeMarker:
3987         case CreateTransportMarker:
3988         case CreateCDMarker:
3989
3990                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3991                         _copy = true;
3992                 } else {
3993                         _copy = false;
3994                 }
3995                 cursor = _editor->cursors()->selector;
3996                 break;
3997         }
3998
3999         Drag::start_grab (event, cursor);
4000
4001         show_verbose_cursor_time (adjusted_current_frame (event));
4002 }
4003
4004 void
4005 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4006 {
4007         framepos_t start = 0;
4008         framepos_t end = 0;
4009         ArdourCanvas::Rectangle *crect;
4010
4011         switch (_operation) {
4012         case CreateRangeMarker:
4013                 crect = _editor->range_bar_drag_rect;
4014                 break;
4015         case CreateTransportMarker:
4016                 crect = _editor->transport_bar_drag_rect;
4017                 break;
4018         case CreateCDMarker:
4019                 crect = _editor->cd_marker_bar_drag_rect;
4020                 break;
4021         default:
4022                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4023                 return;
4024                 break;
4025         }
4026
4027         framepos_t const pf = adjusted_current_frame (event);
4028
4029         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4030                 framepos_t grab = grab_frame ();
4031                 _editor->snap_to (grab);
4032
4033                 if (pf < grab_frame()) {
4034                         start = pf;
4035                         end = grab;
4036                 } else {
4037                         end = pf;
4038                         start = grab;
4039                 }
4040
4041                 /* first drag: Either add to the selection
4042                    or create a new selection.
4043                 */
4044
4045                 if (first_move) {
4046
4047                         _editor->temp_location->set (start, end);
4048
4049                         crect->show ();
4050
4051                         update_item (_editor->temp_location);
4052                         _drag_rect->show();
4053                         //_drag_rect->raise_to_top();
4054
4055                 }
4056         }
4057
4058         if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4059                 _editor->start_canvas_autoscroll (1, 0);
4060         }
4061
4062         if (start != end) {
4063                 _editor->temp_location->set (start, end);
4064
4065                 double x1 = _editor->sample_to_pixel (start);
4066                 double x2 = _editor->sample_to_pixel (end);
4067                 crect->set_x0 (x1);
4068                 crect->set_x1 (x2);
4069
4070                 update_item (_editor->temp_location);
4071         }
4072
4073         show_verbose_cursor_time (pf);
4074
4075 }
4076
4077 void
4078 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4079 {
4080         Location * newloc = 0;
4081         string rangename;
4082         int flags;
4083
4084         if (movement_occurred) {
4085                 motion (event, false);
4086                 _drag_rect->hide();
4087
4088                 switch (_operation) {
4089                 case CreateRangeMarker:
4090                 case CreateCDMarker:
4091                     {
4092                         _editor->begin_reversible_command (_("new range marker"));
4093                         XMLNode &before = _editor->session()->locations()->get_state();
4094                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
4095                         if (_operation == CreateCDMarker) {
4096                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
4097                                 _editor->cd_marker_bar_drag_rect->hide();
4098                         }
4099                         else {
4100                                 flags = Location::IsRangeMarker;
4101                                 _editor->range_bar_drag_rect->hide();
4102                         }
4103                         newloc = new Location (
4104                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4105                                 );
4106
4107                         _editor->session()->locations()->add (newloc, true);
4108                         XMLNode &after = _editor->session()->locations()->get_state();
4109                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4110                         _editor->commit_reversible_command ();
4111                         break;
4112                     }
4113
4114                 case CreateTransportMarker:
4115                         // popup menu to pick loop or punch
4116                         _editor->new_transport_marker_context_menu (&event->button, _item);
4117                         break;
4118                 }
4119         } else {
4120                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4121
4122                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4123
4124                         framepos_t start;
4125                         framepos_t end;
4126
4127                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4128
4129                         if (end == max_framepos) {
4130                                 end = _editor->session()->current_end_frame ();
4131                         }
4132
4133                         if (start == max_framepos) {
4134                                 start = _editor->session()->current_start_frame ();
4135                         }
4136
4137                         switch (_editor->mouse_mode) {
4138                         case MouseObject:
4139                                 /* find the two markers on either side and then make the selection from it */
4140                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4141                                 break;
4142
4143                         case MouseRange:
4144                                 /* find the two markers on either side of the click and make the range out of it */
4145                                 _editor->selection->set (start, end);
4146                                 break;
4147
4148                         default:
4149                                 break;
4150                         }
4151                 }
4152         }
4153
4154         _editor->stop_canvas_autoscroll ();
4155 }
4156
4157 void
4158 RangeMarkerBarDrag::aborted (bool)
4159 {
4160         /* XXX: TODO */
4161 }
4162
4163 void
4164 RangeMarkerBarDrag::update_item (Location* location)
4165 {
4166         double const x1 = _editor->sample_to_pixel (location->start());
4167         double const x2 = _editor->sample_to_pixel (location->end());
4168
4169         _drag_rect->set_x0 (x1);
4170         _drag_rect->set_x1 (x2);
4171 }
4172
4173 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4174         : Drag (e, i)
4175         , _zoom_out (false)
4176 {
4177         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4178 }
4179
4180 void
4181 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4182 {
4183         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4184                 Drag::start_grab (event, _editor->cursors()->zoom_out);
4185                 _zoom_out = true;
4186         } else {
4187                 Drag::start_grab (event, _editor->cursors()->zoom_in);
4188                 _zoom_out = false;
4189         }
4190
4191         show_verbose_cursor_time (adjusted_current_frame (event));
4192 }
4193
4194 void
4195 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4196 {
4197         framepos_t start;
4198         framepos_t end;
4199
4200         framepos_t const pf = adjusted_current_frame (event);
4201
4202         framepos_t grab = grab_frame ();
4203         _editor->snap_to_with_modifier (grab, event);
4204
4205         /* base start and end on initial click position */
4206         if (pf < grab) {
4207                 start = pf;
4208                 end = grab;
4209         } else {
4210                 end = pf;
4211                 start = grab;
4212         }
4213
4214         if (start != end) {
4215
4216                 if (first_move) {
4217                         _editor->zoom_rect->show();
4218                         _editor->zoom_rect->raise_to_top();
4219                 }
4220
4221                 _editor->reposition_zoom_rect(start, end);
4222
4223                 show_verbose_cursor_time (pf);
4224         }
4225 }
4226
4227 void
4228 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4229 {
4230         if (movement_occurred) {
4231                 motion (event, false);
4232
4233                 if (grab_frame() < last_pointer_frame()) {
4234                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4235                 } else {
4236                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4237                 }
4238         } else {
4239                 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4240                         _editor->tav_zoom_step (_zoom_out);
4241                 } else {
4242                         _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4243                 }
4244         }
4245
4246         _editor->zoom_rect->hide();
4247 }
4248
4249 void
4250 MouseZoomDrag::aborted (bool)
4251 {
4252         _editor->zoom_rect->hide ();
4253 }
4254
4255 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4256         : Drag (e, i)
4257         , _cumulative_dx (0)
4258         , _cumulative_dy (0)
4259 {
4260         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4261
4262         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4263         assert (_primary);
4264         _region = &_primary->region_view ();
4265         _note_height = _region->midi_stream_view()->note_height ();
4266 }
4267
4268 void
4269 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4270 {
4271         Drag::start_grab (event);
4272
4273         if (!(_was_selected = _primary->selected())) {
4274
4275                 /* tertiary-click means extend selection - we'll do that on button release,
4276                    so don't add it here, because otherwise we make it hard to figure
4277                    out the "extend-to" range.
4278                 */
4279
4280                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4281
4282                 if (!extend) {
4283                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4284
4285                         if (add) {
4286                                 _region->note_selected (_primary, true);
4287                         } else {
4288                                 _region->unique_select (_primary);
4289                         }
4290                 }
4291         }
4292 }
4293
4294 /** @return Current total drag x change in frames */
4295 frameoffset_t
4296 NoteDrag::total_dx () const
4297 {
4298         /* dx in frames */
4299         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4300
4301         /* primary note time */
4302         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4303
4304         /* new time of the primary note in session frames */
4305         frameoffset_t st = n + dx;
4306
4307         framepos_t const rp = _region->region()->position ();
4308
4309         /* prevent the note being dragged earlier than the region's position */
4310         st = max (st, rp);
4311
4312         /* snap and return corresponding delta */
4313         return _region->snap_frame_to_frame (st - rp) + rp - n;
4314 }
4315
4316 /** @return Current total drag y change in note number */
4317 int8_t
4318 NoteDrag::total_dy () const
4319 {
4320         MidiStreamView* msv = _region->midi_stream_view ();
4321         double const y = _region->midi_view()->y_position ();
4322         /* new current note */
4323         uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4324         /* clamp */
4325         n = max (msv->lowest_note(), n);
4326         n = min (msv->highest_note(), n);
4327         /* and work out delta */
4328         return n - msv->y_to_note (grab_y() - y);
4329 }
4330
4331 void
4332 NoteDrag::motion (GdkEvent *, bool)
4333 {
4334         /* Total change in x and y since the start of the drag */
4335         frameoffset_t const dx = total_dx ();
4336         int8_t const dy = total_dy ();
4337
4338         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4339         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4340         double const tdy = -dy * _note_height - _cumulative_dy;
4341
4342         if (tdx || tdy) {
4343                 _cumulative_dx += tdx;
4344                 _cumulative_dy += tdy;
4345
4346                 int8_t note_delta = total_dy();
4347
4348                 _region->move_selection (tdx, tdy, note_delta);
4349
4350                 /* the new note value may be the same as the old one, but we
4351                  * don't know what that means because the selection may have
4352                  * involved more than one note and we might be doing something
4353                  * odd with them. so show the note value anyway, always.
4354                  */
4355
4356                 char buf[12];
4357                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4358                 
4359                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4360                           (int) floor ((double)new_note));
4361
4362                 show_verbose_cursor_text (buf);
4363         }
4364 }
4365
4366 void
4367 NoteDrag::finished (GdkEvent* ev, bool moved)
4368 {
4369         if (!moved) {
4370                 /* no motion - select note */
4371                 
4372                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4373                     _editor->current_mouse_mode() == Editing::MouseDraw) {
4374                         
4375                         if (_was_selected) {
4376                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4377                                 if (add) {
4378                                         _region->note_deselected (_primary);
4379                                 }
4380                         } else {
4381                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4382                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4383
4384                                 if (!extend && !add && _region->selection_size() > 1) {
4385                                         _region->unique_select (_primary);
4386                                 } else if (extend) {
4387                                         _region->note_selected (_primary, true, true);
4388                                 } else {
4389                                         /* it was added during button press */
4390                                 }
4391                         }
4392                 }
4393         } else {
4394                 _region->note_dropped (_primary, total_dx(), total_dy());
4395         }
4396 }
4397
4398 void
4399 NoteDrag::aborted (bool)
4400 {
4401         /* XXX: TODO */
4402 }
4403
4404 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4405 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4406         : Drag (editor, atv->base_item ())
4407         , _ranges (r)
4408         , _nothing_to_drag (false)
4409 {
4410         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4411         y_origin = atv->y_position();
4412         setup (atv->lines ());
4413 }
4414
4415 /** Make an AutomationRangeDrag for region gain lines */
4416 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4417         : Drag (editor, rv->get_canvas_group ())
4418         , _ranges (r)
4419         , _nothing_to_drag (false)
4420 {
4421         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4422
4423         list<boost::shared_ptr<AutomationLine> > lines;
4424         lines.push_back (rv->get_gain_line ());
4425         y_origin = rv->get_time_axis_view().y_position();
4426         setup (lines);
4427 }
4428
4429 /** @param lines AutomationLines to drag.
4430  *  @param offset Offset from the session start to the points in the AutomationLines.
4431  */
4432 void
4433 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4434 {
4435         /* find the lines that overlap the ranges being dragged */
4436         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4437         while (i != lines.end ()) {
4438                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4439                 ++j;
4440
4441                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4442
4443                 /* check this range against all the AudioRanges that we are using */
4444                 list<AudioRange>::const_iterator k = _ranges.begin ();
4445                 while (k != _ranges.end()) {
4446                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4447                                 break;
4448                         }
4449                         ++k;
4450                 }
4451
4452                 /* add it to our list if it overlaps at all */
4453                 if (k != _ranges.end()) {
4454                         Line n;
4455                         n.line = *i;
4456                         n.state = 0;
4457                         n.range = r;
4458                         _lines.push_back (n);
4459                 }
4460
4461                 i = j;
4462         }
4463
4464         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4465 }
4466
4467 double
4468 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4469 {
4470         return 1.0 - ((global_y - y_origin) / line->height());
4471 }
4472
4473 void
4474 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4475 {
4476         Drag::start_grab (event, cursor);
4477
4478         /* Get line states before we start changing things */
4479         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4480                 i->state = &i->line->get_state ();
4481                 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4482         }
4483
4484         if (_ranges.empty()) {
4485
4486                 /* No selected time ranges: drag all points */
4487                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4488                         uint32_t const N = i->line->npoints ();
4489                         for (uint32_t j = 0; j < N; ++j) {
4490                                 i->points.push_back (i->line->nth (j));
4491                         }
4492                 }
4493
4494         } else {
4495
4496                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4497
4498                         framecnt_t const half = (i->start + i->end) / 2;
4499
4500                         /* find the line that this audio range starts in */
4501                         list<Line>::iterator j = _lines.begin();
4502                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4503                                 ++j;
4504                         }
4505
4506                         if (j != _lines.end()) {
4507                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4508
4509                                 /* j is the line that this audio range starts in; fade into it;
4510                                    64 samples length plucked out of thin air.
4511                                 */
4512
4513                                 framepos_t a = i->start + 64;
4514                                 if (a > half) {
4515                                         a = half;
4516                                 }
4517
4518                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4519                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4520
4521                                 the_list->add (p, the_list->eval (p));
4522                                 the_list->add (q, the_list->eval (q));
4523                         }
4524
4525                         /* same thing for the end */
4526
4527                         j = _lines.begin();
4528                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4529                                 ++j;
4530                         }
4531
4532                         if (j != _lines.end()) {
4533                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4534
4535                                 /* j is the line that this audio range starts in; fade out of it;
4536                                    64 samples length plucked out of thin air.
4537                                 */
4538
4539                                 framepos_t b = i->end - 64;
4540                                 if (b < half) {
4541                                         b = half;
4542                                 }
4543
4544                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4545                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4546
4547                                 the_list->add (p, the_list->eval (p));
4548                                 the_list->add (q, the_list->eval (q));
4549                         }
4550                 }
4551
4552                 _nothing_to_drag = true;
4553
4554                 /* Find all the points that should be dragged and put them in the relevant
4555                    points lists in the Line structs.
4556                 */
4557
4558                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4559
4560                         uint32_t const N = i->line->npoints ();
4561                         for (uint32_t j = 0; j < N; ++j) {
4562
4563                                 /* here's a control point on this line */
4564                                 ControlPoint* p = i->line->nth (j);
4565                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4566
4567                                 /* see if it's inside a range */
4568                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4569                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4570                                         ++k;
4571                                 }
4572
4573                                 if (k != _ranges.end()) {
4574                                         /* dragging this point */
4575                                         _nothing_to_drag = false;
4576                                         i->points.push_back (p);
4577                                 }
4578                         }
4579                 }
4580         }
4581
4582         if (_nothing_to_drag) {
4583                 return;
4584         }
4585
4586         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4587                 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4588         }
4589 }
4590
4591 void
4592 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4593 {
4594         if (_nothing_to_drag) {
4595                 return;
4596         }
4597
4598         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4599                 float const f = y_fraction (l->line, _drags->current_pointer_y());
4600                 /* we are ignoring x position for this drag, so we can just pass in anything */
4601                 uint32_t ignored;
4602                 l->line->drag_motion (0, f, true, false, ignored);
4603                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4604         }
4605 }
4606
4607 void
4608 AutomationRangeDrag::finished (GdkEvent* event, bool)
4609 {
4610         if (_nothing_to_drag) {
4611                 return;
4612         }
4613
4614         motion (event, false);
4615         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4616                 i->line->end_drag (false, 0);
4617         }
4618
4619         _editor->session()->commit_reversible_command ();
4620 }
4621
4622 void
4623 AutomationRangeDrag::aborted (bool)
4624 {
4625         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4626                 i->line->reset ();
4627         }
4628 }
4629
4630 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4631         : view (v)
4632 {
4633         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4634         layer = v->region()->layer ();
4635         initial_y = v->get_canvas_group()->position().y;
4636         initial_playlist = v->region()->playlist ();
4637         initial_position = v->region()->position ();
4638         initial_end = v->region()->position () + v->region()->length ();
4639 }
4640
4641 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4642         : Drag (e, i->canvas_item ())
4643         , _region_view (r)
4644         , _patch_change (i)
4645         , _cumulative_dx (0)
4646 {
4647         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4648                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4649                                                    grab_frame()));
4650 }
4651
4652 void
4653 PatchChangeDrag::motion (GdkEvent* ev, bool)
4654 {
4655         framepos_t f = adjusted_current_frame (ev);
4656         boost::shared_ptr<Region> r = _region_view->region ();
4657         f = max (f, r->position ());
4658         f = min (f, r->last_frame ());
4659
4660         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4661         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4662         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4663         _cumulative_dx = dxu;
4664 }
4665
4666 void
4667 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4668 {
4669         if (!movement_occurred) {
4670                 return;
4671         }
4672
4673         boost::shared_ptr<Region> r (_region_view->region ());
4674         framepos_t f = adjusted_current_frame (ev);
4675         f = max (f, r->position ());
4676         f = min (f, r->last_frame ());
4677
4678         _region_view->move_patch_change (
4679                 *_patch_change,
4680                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4681                 );
4682 }
4683
4684 void
4685 PatchChangeDrag::aborted (bool)
4686 {
4687         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4688 }
4689
4690 void
4691 PatchChangeDrag::setup_pointer_frame_offset ()
4692 {
4693         boost::shared_ptr<Region> region = _region_view->region ();
4694         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4695 }
4696
4697 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4698         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4699         , _region_view (rv)
4700 {
4701
4702 }
4703
4704 void
4705 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4706 {
4707         framepos_t const p = _region_view->region()->position ();
4708         double const y = _region_view->midi_view()->y_position ();
4709
4710         x1 = max ((framepos_t) 0, x1 - p);
4711         x2 = max ((framepos_t) 0, x2 - p);
4712         y1 = max (0.0, y1 - y);
4713         y2 = max (0.0, y2 - y);
4714         
4715         _region_view->update_drag_selection (
4716                 _editor->sample_to_pixel (x1),
4717                 _editor->sample_to_pixel (x2),
4718                 y1,
4719                 y2,
4720                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4721                 );
4722 }
4723
4724 void
4725 MidiRubberbandSelectDrag::deselect_things ()
4726 {
4727         /* XXX */
4728 }
4729
4730 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4731         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4732         , _region_view (rv)
4733 {
4734         _vertical_only = true;
4735 }
4736
4737 void
4738 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4739 {
4740         double const y = _region_view->midi_view()->y_position ();
4741
4742         y1 = max (0.0, y1 - y);
4743         y2 = max (0.0, y2 - y);
4744         
4745         _region_view->update_vertical_drag_selection (
4746                 y1,
4747                 y2,
4748                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4749                 );
4750 }
4751
4752 void
4753 MidiVerticalSelectDrag::deselect_things ()
4754 {
4755         /* XXX */
4756 }
4757
4758 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4759         : RubberbandSelectDrag (e, i)
4760 {
4761
4762 }
4763
4764 void
4765 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4766 {
4767         if (drag_in_progress) {
4768                 /* We just want to select things at the end of the drag, not during it */
4769                 return;
4770         }
4771         
4772         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4773         
4774         _editor->begin_reversible_command (_("rubberband selection"));
4775         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4776         _editor->commit_reversible_command ();
4777 }
4778
4779 void
4780 EditorRubberbandSelectDrag::deselect_things ()
4781 {
4782         if (!getenv("ARDOUR_SAE")) {
4783                 _editor->selection->clear_tracks();
4784         }
4785         _editor->selection->clear_regions();
4786         _editor->selection->clear_points ();
4787         _editor->selection->clear_lines ();
4788 }
4789
4790 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4791         : Drag (e, i)
4792         , _region_view (rv)
4793         , _drag_rect (0)
4794 {
4795         
4796 }
4797
4798 NoteCreateDrag::~NoteCreateDrag ()
4799 {
4800         delete _drag_rect;
4801 }
4802
4803 framecnt_t
4804 NoteCreateDrag::grid_frames (framepos_t t) const
4805 {
4806         bool success;
4807         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4808         if (!success) {
4809                 grid_beats = 1;
4810         }
4811
4812         return _region_view->region_beats_to_region_frames (grid_beats);
4813 }
4814
4815 void
4816 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4817 {
4818         Drag::start_grab (event, cursor);
4819                                                  
4820         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4821
4822         framepos_t pf = _drags->current_pointer_frame ();
4823         framecnt_t const g = grid_frames (pf);
4824
4825         /* Hack so that we always snap to the note that we are over, instead of snapping
4826            to the next one if we're more than halfway through the one we're over.
4827         */
4828         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4829                 pf -= g / 2;
4830         }
4831
4832         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4833
4834         MidiStreamView* sv = _region_view->midi_stream_view ();
4835         double const x = _editor->sample_to_pixel (_note[0]);
4836         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4837
4838         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4839         _drag_rect->set_outline_what (0xff);
4840         _drag_rect->set_outline_color (0xffffff99);
4841         _drag_rect->set_fill_color (0xffffff66);
4842 }
4843
4844 void
4845 NoteCreateDrag::motion (GdkEvent* event, bool)
4846 {
4847         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4848         double const x = _editor->sample_to_pixel (_note[1]);
4849         if (_note[1] > _note[0]) {
4850                 _drag_rect->set_x1 (x);
4851         } else {
4852                 _drag_rect->set_x0 (x);
4853         }
4854 }
4855
4856 void
4857 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4858 {
4859         if (!had_movement) {
4860                 return;
4861         }
4862         
4863         framepos_t const start = min (_note[0], _note[1]);
4864         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
4865
4866         framecnt_t const g = grid_frames (start);
4867         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4868         
4869         if (_editor->snap_mode() == SnapNormal && length < g) {
4870                 length = g - one_tick;
4871         }
4872
4873         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4874
4875         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4876 }
4877
4878 double
4879 NoteCreateDrag::y_to_region (double y) const
4880 {
4881         double x = 0;
4882         _region_view->get_canvas_group()->canvas_to_item (x, y);
4883         return y;
4884 }
4885
4886 void
4887 NoteCreateDrag::aborted (bool)
4888 {
4889         
4890 }
4891
4892 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4893         : Drag (e, i)
4894         , arv (rv)
4895         , start (start_yn)
4896 {
4897         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
4898 }
4899
4900 void
4901 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4902 {
4903         Drag::start_grab (event, cursor);
4904 }
4905
4906 void
4907 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4908 {
4909         double distance;
4910         double new_length;
4911         framecnt_t len;
4912
4913         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4914
4915         if (start) {
4916                 distance = _drags->current_pointer_x() - grab_x();
4917                 len = ar->fade_in()->back()->when;
4918         } else {
4919                 distance = grab_x() - _drags->current_pointer_x();
4920                 len = ar->fade_out()->back()->when;
4921         }
4922
4923         /* how long should it be ? */
4924
4925         new_length = len + _editor->pixel_to_sample (distance);
4926
4927         /* now check with the region that this is legal */
4928
4929         new_length = ar->verify_xfade_bounds (new_length, start);
4930
4931         if (start) {
4932                 arv->reset_fade_in_shape_width (ar, new_length);
4933         } else {
4934                 arv->reset_fade_out_shape_width (ar, new_length);
4935         }
4936 }
4937
4938 void
4939 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4940 {
4941         double distance;
4942         double new_length;
4943         framecnt_t len;
4944
4945         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4946
4947         if (start) {
4948                 distance = _drags->current_pointer_x() - grab_x();
4949                 len = ar->fade_in()->back()->when;
4950         } else {
4951                 distance = grab_x() - _drags->current_pointer_x();
4952                 len = ar->fade_out()->back()->when;
4953         }
4954
4955         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4956         
4957         _editor->begin_reversible_command ("xfade trim");
4958         ar->playlist()->clear_owned_changes (); 
4959
4960         if (start) {
4961                 ar->set_fade_in_length (new_length);
4962         } else {
4963                 ar->set_fade_out_length (new_length);
4964         }
4965
4966         /* Adjusting the xfade may affect other regions in the playlist, so we need
4967            to get undo Commands from the whole playlist rather than just the
4968            region.
4969         */
4970
4971         vector<Command*> cmds;
4972         ar->playlist()->rdiff (cmds);
4973         _editor->session()->add_commands (cmds);
4974         _editor->commit_reversible_command ();
4975
4976 }
4977
4978 void
4979 CrossfadeEdgeDrag::aborted (bool)
4980 {
4981         if (start) {
4982                 arv->redraw_start_xfade ();
4983         } else {
4984                 arv->redraw_end_xfade ();
4985         }
4986 }
4987