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