e75bfba5768c8ff701ad00d05cdcb587cf0cd262
[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 #define __STDC_LIMIT_MACROS 1
21 #include <stdint.h>
22 #include "pbd/memento_command.h"
23 #include "pbd/basename.h"
24 #include "pbd/stateful_diff_command.h"
25 #include "ardour/session.h"
26 #include "ardour/dB.h"
27 #include "ardour/region_factory.h"
28 #include "editor.h"
29 #include "i18n.h"
30 #include "keyboard.h"
31 #include "audio_region_view.h"
32 #include "midi_region_view.h"
33 #include "ardour_ui.h"
34 #include "gui_thread.h"
35 #include "control_point.h"
36 #include "utils.h"
37 #include "region_gain_line.h"
38 #include "editor_drag.h"
39 #include "audio_time_axis.h"
40 #include "midi_time_axis.h"
41 #include "canvas-note.h"
42 #include "selection.h"
43 #include "midi_selection.h"
44 #include "automation_time_axis.h"
45
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
49 using namespace Gtk;
50 using namespace Editing;
51 using namespace ArdourCanvas;
52
53 using Gtkmm2ext::Keyboard;
54
55 double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0));
56
57 DragManager::DragManager (Editor* e)
58         : _editor (e)
59         , _ending (false)
60         , _current_pointer_frame (0)
61 {
62
63 }
64
65 DragManager::~DragManager ()
66 {
67         abort ();
68 }
69
70 /** Call abort for each active drag */
71 void
72 DragManager::abort ()
73 {
74         _ending = true;
75         
76         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
77                 (*i)->abort ();
78                 delete *i;
79         }
80
81         _drags.clear ();
82
83         _ending = false;
84 }
85
86 void
87 DragManager::add (Drag* d)
88 {
89         d->set_manager (this);
90         _drags.push_back (d);
91 }
92
93 void
94 DragManager::set (Drag* d, GdkEvent* e, Gdk::Cursor* c)
95 {
96         assert (_drags.empty ());
97         d->set_manager (this);
98         _drags.push_back (d);
99         start_grab (e, c);
100 }
101
102 void
103 DragManager::start_grab (GdkEvent* e, Gdk::Cursor* c)
104 {
105         _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
106         
107         for (list<Drag*>::const_iterator i = _drags.begin(); i != _drags.end(); ++i) {
108                 (*i)->start_grab (e, c);
109         }
110 }
111
112 /** Call end_grab for each active drag.
113  *  @return true if any drag reported movement having occurred.
114  */
115 bool
116 DragManager::end_grab (GdkEvent* e)
117 {
118         _ending = true;
119         
120         bool r = false;
121         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
122                 bool const t = (*i)->end_grab (e);
123                 if (t) {
124                         r = true;
125                 }
126                 delete *i;
127         }
128
129         _drags.clear ();
130
131         _ending = false;
132         
133         return r;
134 }
135
136 bool
137 DragManager::motion_handler (GdkEvent* e, bool from_autoscroll)
138 {
139         bool r = false;
140
141         _current_pointer_frame = _editor->event_frame (e, &_current_pointer_x, &_current_pointer_y);
142         
143         for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
144                 bool const t = (*i)->motion_handler (e, from_autoscroll);
145                 if (t) {
146                         r = true;
147                 }
148                 
149         }
150
151         return r;
152 }
153
154 bool
155 DragManager::have_item (ArdourCanvas::Item* i) const
156 {
157         list<Drag*>::const_iterator j = _drags.begin ();
158         while (j != _drags.end() && (*j)->item () != i) {
159                 ++j;
160         }
161
162         return j != _drags.end ();
163 }
164
165 Drag::Drag (Editor* e, ArdourCanvas::Item* i) 
166         : _editor (e)
167         , _item (i)
168         , _pointer_frame_offset (0)
169         , _move_threshold_passed (false)
170         , _raw_grab_frame (0)
171         , _grab_frame (0)
172         , _last_pointer_frame (0)
173 {
174
175 }
176
177 void
178 Drag::swap_grab (ArdourCanvas::Item* new_item, Gdk::Cursor* cursor, uint32_t time)
179 {
180         _item->ungrab (0);
181         _item = new_item;
182
183         if (cursor == 0) {
184                 cursor = _editor->which_grabber_cursor ();
185         }
186
187         _item->grab (Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK, *cursor, time);
188 }
189
190 void
191 Drag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
192 {
193         if (cursor == 0) {
194                 cursor = _editor->which_grabber_cursor ();
195         }
196
197         // if dragging with button2, the motion is x constrained, with Alt-button2 it is y constrained
198
199         if (Keyboard::is_button2_event (&event->button)) {
200                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
201                         _y_constrained = true;
202                         _x_constrained = false;
203                 } else {
204                         _y_constrained = false;
205                         _x_constrained = true;
206                 }
207         } else {
208                 _x_constrained = false;
209                 _y_constrained = false;
210         }
211
212         _raw_grab_frame = _editor->event_frame (event, &_grab_x, &_grab_y);
213         _grab_frame = adjusted_frame (_raw_grab_frame, event);
214         _last_pointer_frame = _grab_frame;
215         _last_pointer_x = _grab_x;
216         _last_pointer_y = _grab_y;
217
218         _item->grab (Gdk::POINTER_MOTION_MASK|Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK,
219                      *cursor,
220                      event->button.time);
221
222         if (_editor->session() && _editor->session()->transport_rolling()) {
223                 _was_rolling = true;
224         } else {
225                 _was_rolling = false;
226         }
227
228         switch (_editor->snap_type()) {
229         case SnapToRegionStart:
230         case SnapToRegionEnd:
231         case SnapToRegionSync:
232         case SnapToRegionBoundary:
233                 _editor->build_region_boundary_cache ();
234                 break;
235         default:
236                 break;
237         }
238 }
239
240 /** Call to end a drag `successfully'.  Ungrabs item and calls
241  *  subclass' finished() method.
242  *
243  *  @param event GDK event, or 0.
244  *  @return true if some movement occurred, otherwise false.
245  */
246 bool
247 Drag::end_grab (GdkEvent* event)
248 {
249         _editor->stop_canvas_autoscroll ();
250
251         _item->ungrab (event ? event->button.time : 0);
252
253         finished (event, _move_threshold_passed);
254
255         _editor->hide_verbose_canvas_cursor();
256
257         return _move_threshold_passed;
258 }
259
260 nframes64_t
261 Drag::adjusted_frame (nframes64_t f, GdkEvent const * event, bool snap) const
262 {
263         nframes64_t pos = 0;
264
265         if (f > _pointer_frame_offset) {
266                 pos = f - _pointer_frame_offset;
267         }
268
269         if (snap) {
270                 _editor->snap_to_with_modifier (pos, event);
271         }
272
273         return pos;
274 }
275
276 nframes64_t
277 Drag::adjusted_current_frame (GdkEvent const * event, bool snap) const
278 {
279         return adjusted_frame (_drags->current_pointer_frame (), event, snap);
280 }
281
282 bool
283 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
284 {
285         /* check to see if we have moved in any way that matters since the last motion event */
286         if ( (!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
287              (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
288                 return false;
289         }
290
291         pair<nframes64_t, int> const threshold = move_threshold ();
292
293         bool const old_move_threshold_passed = _move_threshold_passed;
294
295         if (!from_autoscroll && !_move_threshold_passed) {
296
297                 bool const xp = (::llabs (adjusted_current_frame (event) - _grab_frame) >= threshold.first);
298                 bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
299
300                 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
301         }
302
303         if (active (_editor->mouse_mode) && _move_threshold_passed) {
304
305                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
306                         if (!from_autoscroll) {
307                                 _editor->maybe_autoscroll (true, allow_vertical_autoscroll ());
308                         }
309
310                         motion (event, _move_threshold_passed != old_move_threshold_passed);
311
312                         _last_pointer_x = _drags->current_pointer_x ();
313                         _last_pointer_y = _drags->current_pointer_y ();
314                         _last_pointer_frame = adjusted_current_frame (event);
315                         
316                         return true;
317                 }
318         }
319         return false;
320 }
321
322 /** Call to abort a drag.  Ungrabs item and calls subclass's aborted () */
323 void
324 Drag::abort ()
325 {
326         if (_item) {
327                 _item->ungrab (0);
328         }
329
330         aborted ();
331
332         _editor->stop_canvas_autoscroll ();
333         _editor->hide_verbose_canvas_cursor ();
334 }
335
336 struct EditorOrderTimeAxisViewSorter {
337     bool operator() (TimeAxisView* a, TimeAxisView* b) {
338             RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
339             RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
340             assert (ra && rb);
341             return ra->route()->order_key (N_ ("editor")) < rb->route()->order_key (N_ ("editor"));
342     }
343 };
344
345 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
346         : Drag (e, i),
347           _primary (p)
348 {
349         _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
350
351         /* Make a list of non-hidden tracks to refer to during the drag */
352
353         TrackViewList track_views = _editor->track_views;
354         track_views.sort (EditorOrderTimeAxisViewSorter ());
355
356         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
357                 if (!(*i)->hidden()) {
358                         
359                         _time_axis_views.push_back (*i);
360
361                         TimeAxisView::Children children_list = (*i)->get_child_list ();
362                         for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
363                                 _time_axis_views.push_back (j->get());
364                         }
365                 }
366         }
367         
368         for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
369                 _views.push_back (DraggingView (*i, this));
370         }
371         
372         RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), ui_bind (&RegionDrag::region_going_away, this, _1), gui_context());
373 }
374
375 void
376 RegionDrag::region_going_away (RegionView* v)
377 {
378         list<DraggingView>::iterator i = _views.begin ();
379         while (i != _views.end() && i->view != v) {
380                 ++i;
381         }
382
383         if (i != _views.end()) {
384                 _views.erase (i);
385         }
386 }
387
388 /** Given a non-hidden TimeAxisView, return the index of it into the _time_axis_views vector */
389 int
390 RegionDrag::find_time_axis_view (TimeAxisView* t) const
391 {
392         int i = 0;
393         int const N = _time_axis_views.size ();
394         while (i < N && _time_axis_views[i] != t) {
395                 ++i;
396         }
397
398         if (i == N) {
399                 return -1;
400         }
401         
402         return i;
403 }
404
405 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
406         : RegionDrag (e, i, p, v),
407           _brushing (b),
408           _total_x_delta (0)
409 {
410
411 }
412
413
414 void
415 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
416 {
417         Drag::start_grab (event, cursor);
418
419         _editor->show_verbose_time_cursor (_last_frame_position, 10);
420
421         pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
422         _last_pointer_time_axis_view = find_time_axis_view (tv.first);
423         _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
424 }
425
426 double
427 RegionMotionDrag::compute_x_delta (GdkEvent const * event, nframes64_t* pending_region_position)
428 {
429         /* compute the amount of pointer motion in frames, and where
430            the region would be if we moved it by that much.
431         */
432         *pending_region_position = adjusted_current_frame (event);
433
434         nframes64_t sync_frame;
435         nframes64_t sync_offset;
436         int32_t sync_dir;
437         
438         sync_offset = _primary->region()->sync_offset (sync_dir);
439         
440         /* we don't handle a sync point that lies before zero.
441          */
442         if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
443                 
444                 sync_frame = *pending_region_position + (sync_dir*sync_offset);
445                 
446                 _editor->snap_to_with_modifier (sync_frame, event);
447                 
448                 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
449                 
450         } else {
451                 *pending_region_position = _last_frame_position;
452         }
453
454         if (*pending_region_position > max_frames - _primary->region()->length()) {
455                 *pending_region_position = _last_frame_position;
456         }
457
458         double dx = 0;
459
460         /* in locked edit mode, reverse the usual meaning of _x_constrained */
461         bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
462
463         if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
464
465                 /* x movement since last time */
466                 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->frames_per_unit;
467
468                 /* total x movement */
469                 framecnt_t total_dx = *pending_region_position;
470                 if (regions_came_from_canvas()) {
471                         total_dx = total_dx - grab_frame () + _pointer_frame_offset;
472                 }
473
474                 /* check that no regions have gone off the start of the session */
475                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
476                         if ((i->view->region()->position() + total_dx) < 0) {
477                                 dx = 0;
478                                 *pending_region_position = _last_frame_position;
479                                 break;
480                         }
481                 }
482
483                 _last_frame_position = *pending_region_position;
484         }
485
486         return dx;
487 }
488
489 bool
490 RegionMotionDrag::y_movement_allowed (int delta_track, layer_t delta_layer) const
491 {
492         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
493                 int const n = i->time_axis_view + delta_track;
494                 if (n < 0 || n >= int (_time_axis_views.size())) {
495                         /* off the top or bottom track */
496                         return false;
497                 }
498
499                 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
500                 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
501                         /* not a track, or the wrong type */
502                         return false;
503                 }
504                 
505                 int const l = i->layer + delta_layer;
506                 if (delta_track == 0 && (l < 0 || l >= int (to->view()->layers()))) {
507                         /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
508                            If it has, the layers will be munged later anyway, so it's ok.
509                         */
510                         return false;
511                 }
512         }
513
514         /* all regions being dragged are ok with this change */
515         return true;
516 }
517
518 void
519 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
520 {
521         /* Find the TimeAxisView that the pointer is now over */
522         pair<TimeAxisView*, int> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
523
524         /* Bail early if we're not over a track */
525         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
526         if (!rtv || !rtv->is_track()) {
527                 _editor->hide_verbose_canvas_cursor ();
528                 return;
529         }
530
531         /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
532
533         /* Here's the current pointer position in terms of time axis view and layer */
534         int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
535         layer_t const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
536
537         /* Work out the change in x */
538         framepos_t pending_region_position;
539         double const x_delta = compute_x_delta (event, &pending_region_position);
540
541         /* Work out the change in y */
542         int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
543         int delta_layer = current_pointer_layer - _last_pointer_layer;
544
545         if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
546                 /* this y movement is not allowed, so do no y movement this time */
547                 delta_time_axis_view = 0;
548                 delta_layer = 0;
549         }
550
551         if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
552                 /* haven't reached next snap point, and we're not switching
553                    trackviews nor layers. nothing to do.
554                 */
555                 return;
556         }
557
558         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
559
560         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
561
562                 RegionView* rv = i->view;
563
564                 if (rv->region()->locked()) {
565                         continue;
566                 }
567
568                 if (first_move) {
569
570                         /* here we are calculating the y distance from the
571                            top of the first track view to the top of the region
572                            area of the track view that we're working on */
573                         
574                         /* this x value is just a dummy value so that we have something
575                            to pass to i2w () */
576                         
577                         double ix1 = 0;
578                         
579                         /* distance from the top of this track view to the region area
580                            of our track view is always 1 */
581                         
582                         double iy1 = 1;
583                         
584                         /* convert to world coordinates, ie distance from the top of
585                            the ruler section */
586                         
587                         rv->get_canvas_frame()->i2w (ix1, iy1);
588                         
589                         /* compensate for the ruler section and the vertical scrollbar position */
590                         iy1 += _editor->get_trackview_group_vertical_offset ();
591                         
592                         // hide any dependent views
593                         
594                         rv->get_time_axis_view().hide_dependent_views (*rv);
595                         
596                         /*
597                           reparent to a non scrolling group so that we can keep the
598                           region selection above all time axis views.
599                           reparenting means we have to move the rv as the two
600                           parent groups have different coordinates.
601                         */
602                         
603                         rv->get_canvas_group()->property_y() = iy1 - 1;
604                         rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
605                         
606                         rv->fake_set_opaque (true);
607                 }
608
609                 /* Work out the change in y position of this region view */
610
611                 double y_delta = 0;
612
613                 /* If we have moved tracks, we'll fudge the layer delta so that the
614                    region gets moved back onto layer 0 on its new track; this avoids
615                    confusion when dragging regions from non-zero layers onto different
616                    tracks.
617                 */
618                 int this_delta_layer = delta_layer;
619                 if (delta_time_axis_view != 0) {
620                         this_delta_layer = - i->layer;
621                 }
622
623                 /* Move this region to layer 0 on its old track */
624                 StreamView* lv = _time_axis_views[i->time_axis_view]->view ();
625                 if (lv->layer_display() == Stacked) {
626                         y_delta -= (lv->layers() - i->layer - 1) * lv->child_height ();
627                 }
628
629                 /* Now move it to its right layer on the current track */
630                 StreamView* cv = _time_axis_views[i->time_axis_view + delta_time_axis_view]->view ();
631                 if (cv->layer_display() == Stacked) {
632                         y_delta += (cv->layers() - (i->layer + this_delta_layer) - 1) * cv->child_height ();
633                 }
634
635                 /* Move tracks */
636                 if (delta_time_axis_view > 0) {
637                         for (int j = 0; j < delta_time_axis_view; ++j) {
638                                 y_delta += _time_axis_views[i->time_axis_view + j]->current_height ();
639                         }
640                 } else {
641                         /* start by subtracting the height of the track above where we are now */
642                         for (int j = 1; j <= -delta_time_axis_view; ++j) {
643                                 y_delta -= _time_axis_views[i->time_axis_view - j]->current_height ();
644                         }
645                 }
646
647                 /* Set height */
648                 rv->set_height (_time_axis_views[i->time_axis_view + delta_time_axis_view]->view()->child_height ());
649
650                 /* Update the DraggingView */
651                 i->time_axis_view += delta_time_axis_view;
652                 i->layer += this_delta_layer;
653
654                 if (_brushing) {
655                         _editor->mouse_brush_insert_region (rv, pending_region_position);
656                 } else {
657                         rv->move (x_delta, y_delta);
658                 }
659
660         } /* foreach region */
661
662         _total_x_delta += x_delta;
663         
664         if (first_move) {
665                 _editor->cursor_group->raise_to_top();
666         }
667
668         if (x_delta != 0 && !_brushing) {
669                 _editor->show_verbose_time_cursor (_last_frame_position, 10);
670         }
671
672         _last_pointer_time_axis_view += delta_time_axis_view;
673         _last_pointer_layer += delta_layer;
674 }
675
676 void
677 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
678 {
679         if (_copy && first_move) {
680
681                 /* duplicate the regionview(s) and region(s) */
682
683                 list<DraggingView> new_regionviews;
684
685                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
686                         
687                         RegionView* rv = i->view;
688                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
689                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
690
691                         const boost::shared_ptr<const Region> original = rv->region();
692                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
693                         region_copy->set_position (original->position(), this);
694                         
695                         RegionView* nrv;
696                         if (arv) {
697                                 boost::shared_ptr<AudioRegion> audioregion_copy
698                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
699                                 
700                                 nrv = new AudioRegionView (*arv, audioregion_copy);
701                         } else if (mrv) {
702                                 boost::shared_ptr<MidiRegion> midiregion_copy
703                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
704                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
705                         } else {
706                                 continue;
707                         }
708                         
709                         nrv->get_canvas_group()->show ();
710                         new_regionviews.push_back (DraggingView (nrv, this));
711                         
712                         /* swap _primary to the copy */
713                         
714                         if (rv == _primary) {
715                                 _primary = nrv;
716                         }
717                         
718                         /* ..and deselect the one we copied */
719                         
720                         rv->set_selected (false);
721                 }
722                 
723                 if (!new_regionviews.empty()) {
724                         
725                         /* reflect the fact that we are dragging the copies */
726                         
727                         _views = new_regionviews;
728                         
729                         swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
730                         
731                         /*
732                           sync the canvas to what we think is its current state
733                           without it, the canvas seems to
734                           "forget" to update properly after the upcoming reparent()
735                           ..only if the mouse is in rapid motion at the time of the grab.
736                           something to do with regionview creation taking so long?
737                         */
738                         _editor->update_canvas_now();
739                 }
740         }
741
742         RegionMotionDrag::motion (event, first_move);
743 }
744
745 void
746 RegionMoveDrag::finished (GdkEvent *, bool movement_occurred)
747 {
748         if (!movement_occurred) {
749                 /* just a click */
750                 return;
751         }
752
753         /* reverse this here so that we have the correct logic to finalize
754            the drag.
755         */
756
757         if (Config->get_edit_mode() == Lock) {
758                 _x_constrained = !_x_constrained;
759         }
760
761         bool const changed_position = (_last_frame_position != (nframes64_t) (_primary->region()->position()));
762         bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
763         framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
764
765         _editor->update_canvas_now ();
766         
767         if (_copy) {
768                 
769                 finished_copy (
770                         changed_position,
771                         changed_tracks,
772                         drag_delta
773                         );
774                 
775         } else {
776                 
777                 finished_no_copy (
778                         changed_position,
779                         changed_tracks,
780                         drag_delta
781                         );
782                 
783         }
784 }
785
786 void
787 RegionMoveDrag::finished_copy (
788         bool const changed_position,
789         bool const changed_tracks,
790         framecnt_t const drag_delta
791         )
792 {
793         RegionSelection new_views;
794         PlaylistSet modified_playlists;
795         list<RegionView*> views_to_delete;
796
797         if (_brushing) {
798                 /* all changes were made during motion event handlers */
799
800                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
801                         delete i->view;
802                 }
803
804                 _editor->commit_reversible_command ();
805                 return;
806         }
807
808         if (_x_constrained) {
809                 _editor->begin_reversible_command (_("fixed time region copy"));
810         } else {
811                 _editor->begin_reversible_command (_("region copy"));
812         }
813
814         /* insert the regions into their new playlists */
815         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
816
817                 if (i->view->region()->locked()) {
818                         continue;
819                 }
820
821                 nframes64_t where;
822
823                 if (changed_position && !_x_constrained) {
824                         where = i->view->region()->position() - drag_delta;
825                 } else {
826                         where = i->view->region()->position();
827                 }
828
829                 RegionView* new_view = insert_region_into_playlist (
830                         i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
831                         );
832                 
833                 if (new_view == 0) {
834                         continue;
835                 }
836
837                 new_views.push_back (new_view);
838                 
839                 /* we don't need the copied RegionView any more */
840                 views_to_delete.push_back (i->view);
841         }
842
843         /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
844            because when views are deleted they are automagically removed from _views, which messes
845            up the iteration.
846         */
847         for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
848                 delete *i;
849         }
850
851         /* If we've created new regions either by copying or moving 
852            to a new track, we want to replace the old selection with the new ones 
853         */
854
855         if (new_views.size() > 0) {
856                 _editor->selection->set (new_views);
857         }
858
859         /* write commands for the accumulated diffs for all our modified playlists */
860         add_stateful_diff_commands_for_playlists (modified_playlists);
861
862         _editor->commit_reversible_command ();
863 }
864
865 void
866 RegionMoveDrag::finished_no_copy (
867         bool const changed_position,
868         bool const changed_tracks,
869         framecnt_t const drag_delta
870         )
871 {
872         RegionSelection new_views;
873         PlaylistSet modified_playlists;
874         PlaylistSet frozen_playlists;
875
876         if (_brushing) {
877                 /* all changes were made during motion event handlers */
878                 _editor->commit_reversible_command ();
879                 return;
880         }
881
882         if (_x_constrained) {
883                 _editor->begin_reversible_command (_("fixed time region drag"));
884         } else {
885                 _editor->begin_reversible_command (_("region drag"));
886         }
887
888         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
889
890                 RegionView* rv = i->view;
891
892                 RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
893                 layer_t const dest_layer = i->layer;
894
895                 if (rv->region()->locked()) {
896                         ++i;
897                         continue;
898                 }
899
900                 nframes64_t where;
901
902                 if (changed_position && !_x_constrained) {
903                         where = rv->region()->position() - drag_delta;
904                 } else {
905                         where = rv->region()->position();
906                 }
907
908                 if (changed_tracks) {
909
910                         /* insert into new playlist */
911
912                         RegionView* new_view = insert_region_into_playlist (
913                                 RegionFactory::create (rv->region ()), dest_rtv, dest_layer, where, modified_playlists
914                                 );
915
916                         if (new_view == 0) {
917                                 ++i;
918                                 continue;
919                         }
920
921                         new_views.push_back (new_view);
922
923                         /* remove from old playlist */
924
925                         /* the region that used to be in the old playlist is not
926                            moved to the new one - we use a copy of it. as a result,
927                            any existing editor for the region should no longer be
928                            visible.
929                         */
930                         rv->hide_region_editor();
931                         rv->fake_set_opaque (false);
932
933                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
934
935                 } else {
936                         
937                         rv->region()->clear_changes ();
938
939                         /*
940                            motion on the same track. plonk the previously reparented region
941                            back to its original canvas group (its streamview).
942                            No need to do anything for copies as they are fake regions which will be deleted.
943                         */
944
945                         rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
946                         rv->get_canvas_group()->property_y() = i->initial_y;
947                         rv->get_time_axis_view().reveal_dependent_views (*rv);
948
949                         /* just change the model */
950
951                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
952                         
953                         if (dest_rtv->view()->layer_display() == Stacked) {
954                                 rv->region()->set_layer (dest_layer);
955                                 rv->region()->set_pending_explicit_relayer (true);
956                         }
957                         
958                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
959
960                         pair<PlaylistSet::iterator, bool> r = frozen_playlists.insert (playlist);
961
962                         if (r.second) {
963                                 playlist->freeze ();
964                         }
965
966                         /* this movement may result in a crossfade being modified, so we need to get undo
967                            data from the playlist as well as the region.
968                         */
969                         
970                         r = modified_playlists.insert (playlist);
971                         if (r.second) {
972                                 playlist->clear_changes ();
973                         }
974
975                         rv->region()->set_position (where, (void*) this);
976
977                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
978                 }
979
980                 if (changed_tracks) {
981                         
982                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
983                            was selected in all of them, then removing it from a playlist will have removed all
984                            trace of it from _views (i.e. there were N regions selected, we removed 1,
985                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
986                            corresponding regionview, and _views is now empty).
987
988                            This could have invalidated any and all iterators into _views.
989
990                            The heuristic we use here is: if the region selection is empty, break out of the loop
991                            here. if the region selection is not empty, then restart the loop because we know that
992                            we must have removed at least the region(view) we've just been working on as well as any
993                            that we processed on previous iterations.
994
995                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
996                            we can just iterate.
997                         */
998
999                         
1000                         if (_views.empty()) {
1001                                 break;
1002                         } else {
1003                                 i = _views.begin();
1004                         }
1005
1006                 } else {
1007                         ++i;
1008                 }
1009         }
1010
1011         /* If we've created new regions either by copying or moving 
1012            to a new track, we want to replace the old selection with the new ones 
1013         */
1014
1015         if (new_views.size() > 0) {
1016                 _editor->selection->set (new_views);
1017         }
1018
1019         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1020                 (*p)->thaw();
1021         }
1022
1023         /* write commands for the accumulated diffs for all our modified playlists */
1024         add_stateful_diff_commands_for_playlists (modified_playlists);
1025
1026         _editor->commit_reversible_command ();
1027 }
1028
1029 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1030  *  @param region Region to remove.
1031  *  @param playlist playlist To remove from.
1032  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1033  *  that clear_changes () is only called once per playlist.
1034  */
1035 void
1036 RegionMoveDrag::remove_region_from_playlist (
1037         boost::shared_ptr<Region> region,
1038         boost::shared_ptr<Playlist> playlist,
1039         PlaylistSet& modified_playlists
1040         )
1041 {
1042         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1043
1044         if (r.second) {
1045                 playlist->clear_changes ();
1046         }
1047
1048         playlist->remove_region (region);
1049 }
1050         
1051
1052 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1053  *  clearing the playlist's diff history first if necessary.
1054  *  @param region Region to insert.
1055  *  @param dest_rtv Destination RouteTimeAxisView.
1056  *  @param dest_layer Destination layer.
1057  *  @param where Destination position.
1058  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1059  *  that clear_changes () is only called once per playlist. 
1060  *  @return New RegionView, or 0 if no insert was performed.
1061  */
1062 RegionView *
1063 RegionMoveDrag::insert_region_into_playlist (
1064         boost::shared_ptr<Region> region,
1065         RouteTimeAxisView* dest_rtv,
1066         layer_t dest_layer,
1067         framecnt_t where,
1068         PlaylistSet& modified_playlists
1069         )
1070 {
1071         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1072         if (!dest_playlist) {
1073                 return 0;
1074         }
1075
1076         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1077         _new_region_view = 0;
1078         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1079
1080         /* clear history for the playlist we are about to insert to, provided we haven't already done so */     
1081         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1082         if (r.second) {
1083                 dest_playlist->clear_changes ();
1084         }
1085
1086         dest_playlist->add_region (region, where);
1087
1088         if (dest_rtv->view()->layer_display() == Stacked) {
1089                 region->set_layer (dest_layer);
1090                 region->set_pending_explicit_relayer (true);
1091         }
1092
1093         c.disconnect ();
1094
1095         assert (_new_region_view);
1096
1097         return _new_region_view;
1098 }
1099
1100 void
1101 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1102 {
1103         _new_region_view = rv;
1104 }
1105
1106 void
1107 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1108 {
1109         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1110                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1111                 if (!c->empty()) {
1112                         _editor->session()->add_command (new StatefulDiffCommand (*i));
1113                 } else {
1114                         delete c;
1115                 }
1116         }
1117 }
1118
1119
1120 void
1121 RegionMoveDrag::aborted ()
1122 {
1123         if (_copy) {
1124
1125                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1126                         delete i->view;
1127                 }
1128
1129                 _views.clear ();
1130
1131         } else {
1132                 RegionMotionDrag::aborted ();
1133         }
1134 }
1135
1136 void
1137 RegionMotionDrag::aborted ()
1138 {
1139         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1140                 RegionView* rv = i->view;
1141                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1142                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1143                 assert (rtv);
1144                 rv->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1145                 rv->get_canvas_group()->property_y() = 0;
1146                 rv->get_time_axis_view().reveal_dependent_views (*rv);
1147                 rv->fake_set_opaque (false);
1148                 rv->move (-_total_x_delta, 0);
1149                 rv->set_height (rtv->view()->child_height ());
1150         }
1151
1152         _editor->update_canvas_now ();
1153 }
1154                                       
1155 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1156         : RegionMotionDrag (e, i, p, v, b),
1157           _copy (c)
1158 {
1159         double speed = 1;
1160         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1161         if (rtv && rtv->is_track()) {
1162                 speed = rtv->track()->speed ();
1163         }
1164
1165         _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1166 }
1167
1168 void
1169 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1170 {
1171         RegionMotionDrag::start_grab (event, c);
1172
1173         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1174 }
1175
1176 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1177         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1178 {
1179         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1180                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1181
1182         _primary = v->view()->create_region_view (r, false, false);
1183
1184         _primary->get_canvas_group()->show ();
1185         _primary->set_position (pos, 0);
1186         _views.push_back (DraggingView (_primary, this));
1187
1188         _last_frame_position = pos;
1189
1190         _item = _primary->get_canvas_group ();
1191 }
1192
1193 void
1194 RegionInsertDrag::finished (GdkEvent *, bool)
1195 {
1196         _editor->update_canvas_now ();
1197
1198         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1199
1200         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1201         _primary->get_canvas_group()->property_y() = 0;
1202
1203         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1204
1205         _editor->begin_reversible_command (_("insert region"));
1206         playlist->clear_changes ();
1207         playlist->add_region (_primary->region (), _last_frame_position);
1208         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1209         _editor->commit_reversible_command ();
1210
1211         delete _primary;
1212         _primary = 0;
1213         _views.clear ();
1214 }
1215
1216 void
1217 RegionInsertDrag::aborted ()
1218 {
1219         delete _primary;
1220         _primary = 0;
1221         _views.clear ();
1222 }
1223
1224 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1225         : RegionMoveDrag (e, i, p, v, false, false)
1226 {
1227
1228 }
1229
1230 struct RegionSelectionByPosition {
1231     bool operator() (RegionView*a, RegionView* b) {
1232             return a->region()->position () < b->region()->position();
1233     }
1234 };
1235
1236 void
1237 RegionSpliceDrag::motion (GdkEvent* event, bool)
1238 {
1239         /* Which trackview is this ? */
1240
1241         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1242         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1243         layer_t layer = tvp.second;
1244
1245         if (tv && tv->layer_display() == Overlaid) {
1246                 layer = 0;
1247         }
1248
1249         /* The region motion is only processed if the pointer is over
1250            an audio track.
1251         */
1252
1253         if (!tv || !tv->is_track()) {
1254                 /* To make sure we hide the verbose canvas cursor when the mouse is
1255                    not held over and audiotrack.
1256                 */
1257                 _editor->hide_verbose_canvas_cursor ();
1258                 return;
1259         }
1260
1261         int dir;
1262
1263         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1264                 dir = 1;
1265         } else {
1266                 dir = -1;
1267         }
1268
1269         RegionSelection copy (_editor->selection->regions);
1270
1271         RegionSelectionByPosition cmp;
1272         copy.sort (cmp);
1273
1274         nframes64_t const pf = adjusted_current_frame (event);
1275
1276         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1277
1278                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1279
1280                 if (!atv) {
1281                         continue;
1282                 }
1283
1284                 boost::shared_ptr<Playlist> playlist;
1285
1286                 if ((playlist = atv->playlist()) == 0) {
1287                         continue;
1288                 }
1289
1290                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1291                         continue;
1292                 }
1293
1294                 if (dir > 0) {
1295                         if (pf < (*i)->region()->last_frame() + 1) {
1296                                 continue;
1297                         }
1298                 } else {
1299                         if (pf > (*i)->region()->first_frame()) {
1300                                 continue;
1301                         }
1302                 }
1303
1304
1305                 playlist->shuffle ((*i)->region(), dir);
1306         }
1307 }
1308
1309 void
1310 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1311 {
1312         RegionMoveDrag::finished (event, movement_occurred);
1313 }
1314
1315 void
1316 RegionSpliceDrag::aborted ()
1317 {
1318         /* XXX: TODO */
1319 }
1320
1321 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1322         : Drag (e, i),
1323           _view (dynamic_cast<MidiTimeAxisView*> (v))
1324 {
1325         assert (_view);
1326 }
1327
1328 void
1329 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1330 {
1331         if (first_move) {
1332                 /* don't use a zero-length region otherwise its region view will be hidden when it is created */
1333                 _region = _view->add_region (grab_frame(), 1, false);
1334         } else {
1335                 framepos_t const f = adjusted_current_frame (event);
1336                 if (f < grab_frame()) {
1337                         _region->set_position (f, this);
1338                 }
1339
1340                 /* again, don't use a zero-length region (see above) */
1341                 framecnt_t const len = abs (f - grab_frame ());
1342                 _region->set_length (len < 1 ? 1 : len, this);
1343         }
1344 }
1345
1346 void
1347 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1348 {
1349         if (movement_occurred) {
1350                 _editor->commit_reversible_command ();
1351         }
1352 }
1353
1354 void
1355 RegionCreateDrag::aborted ()
1356 {
1357         /* XXX */
1358 }
1359
1360 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1361         : Drag (e, i)
1362         , region (0)
1363 {
1364
1365 }
1366
1367 void
1368 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1369 {
1370         Gdk::Cursor* cursor;
1371         ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1372
1373         Drag::start_grab (event);
1374
1375         region = &cnote->region_view();
1376
1377         double const region_start = region->get_position_pixels();
1378         double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1379
1380         if (grab_x() <= middle_point) {
1381                 cursor = _editor->left_side_trim_cursor;
1382                 at_front = true;
1383         } else {
1384                 cursor = _editor->right_side_trim_cursor;
1385                 at_front = false;
1386         }
1387
1388         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1389
1390         if (event->motion.state & Keyboard::PrimaryModifier) {
1391                 relative = false;
1392         } else {
1393                 relative = true;
1394         }
1395
1396         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1397
1398         if (ms.size() > 1) {
1399                 /* has to be relative, may make no sense otherwise */
1400                 relative = true;
1401         }
1402
1403         /* select this note; if it is already selected, preserve the existing selection,
1404            otherwise make this note the only one selected.
1405         */
1406         region->note_selected (cnote, cnote->selected ());
1407
1408         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1409                 MidiRegionSelection::iterator next;
1410                 next = r;
1411                 ++next;
1412                 (*r)->begin_resizing (at_front);
1413                 r = next;
1414         }
1415 }
1416
1417 void
1418 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1419 {
1420         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1421         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1422                 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1423         }
1424 }
1425
1426 void
1427 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1428 {
1429         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1430         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1431                 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNote*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1432         }
1433 }
1434
1435 void
1436 NoteResizeDrag::aborted ()
1437 {
1438         /* XXX: TODO */
1439 }
1440
1441 void
1442 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1443 {
1444
1445 }
1446
1447 void
1448 RegionGainDrag::finished (GdkEvent *, bool)
1449 {
1450
1451 }
1452
1453 void
1454 RegionGainDrag::aborted ()
1455 {
1456         /* XXX: TODO */
1457 }
1458
1459 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1460         : RegionDrag (e, i, p, v)
1461         , _have_transaction (false)
1462 {
1463
1464 }
1465
1466 void
1467 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1468 {
1469         double speed = 1.0;
1470         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1471         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1472
1473         if (tv && tv->is_track()) {
1474                 speed = tv->track()->speed();
1475         }
1476
1477         nframes64_t const region_start = (nframes64_t) (_primary->region()->position() / speed);
1478         nframes64_t const region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1479         nframes64_t const region_length = (nframes64_t) (_primary->region()->length() / speed);
1480
1481         nframes64_t const pf = adjusted_current_frame (event);
1482
1483         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1484                 _operation = ContentsTrim;
1485                 Drag::start_grab (event, _editor->trimmer_cursor);
1486         } else {
1487                 /* These will get overridden for a point trim.*/
1488                 if (pf < (region_start + region_length/2)) {
1489                         /* closer to start */
1490                         _operation = StartTrim;
1491                         Drag::start_grab (event, _editor->left_side_trim_cursor);
1492                 } else {
1493                         /* closer to end */
1494                         _operation = EndTrim;
1495                         Drag::start_grab (event, _editor->right_side_trim_cursor);
1496                 }
1497         }
1498
1499         switch (_operation) {
1500         case StartTrim:
1501                 _editor->show_verbose_time_cursor (region_start, 10);
1502                 break;
1503         case EndTrim:
1504                 _editor->show_verbose_time_cursor (region_end, 10);
1505                 break;
1506         case ContentsTrim:
1507                 _editor->show_verbose_time_cursor (pf, 10);
1508                 break;
1509         }
1510 }
1511
1512 void
1513 TrimDrag::motion (GdkEvent* event, bool first_move)
1514 {
1515         RegionView* rv = _primary;
1516
1517         /* snap modifier works differently here..
1518            its current state has to be passed to the
1519            various trim functions in order to work properly
1520         */
1521
1522         double speed = 1.0;
1523         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1524         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1525         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1526
1527         if (tv && tv->is_track()) {
1528                 speed = tv->track()->speed();
1529         }
1530
1531         nframes64_t const pf = adjusted_current_frame (event);
1532
1533         if (first_move) {
1534
1535                 string trim_type;
1536
1537                 switch (_operation) {
1538                 case StartTrim:
1539                         trim_type = "Region start trim";
1540                         break;
1541                 case EndTrim:
1542                         trim_type = "Region end trim";
1543                         break;
1544                 case ContentsTrim:
1545                         trim_type = "Region content trim";
1546                         break;
1547                 }
1548
1549                 _editor->begin_reversible_command (trim_type);
1550                 _have_transaction = true;
1551
1552                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1553                         RegionView* rv = i->view;
1554                         rv->fake_set_opaque(false);
1555                         rv->enable_display (false);
1556                         rv->region()->clear_changes ();
1557                         rv->region()->suspend_property_changes ();
1558
1559                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1560
1561                         if (arv){
1562                                 arv->temporarily_hide_envelope ();
1563                         }
1564
1565                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1566                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1567
1568                         if (insert_result.second) {
1569                                 pl->freeze();
1570                         }
1571                 }
1572         }
1573
1574         bool non_overlap_trim = false;
1575
1576         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1577                 non_overlap_trim = true;
1578         }
1579
1580         switch (_operation) {
1581         case StartTrim:
1582                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1583                         _editor->single_start_trim (*i->view, pf, non_overlap_trim);
1584                 }
1585                 break;
1586
1587         case EndTrim:
1588                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1589                         _editor->single_end_trim (*i->view, pf, non_overlap_trim);
1590                 }
1591                 break;
1592
1593         case ContentsTrim:
1594                 {
1595                         bool swap_direction = false;
1596
1597                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1598                                 swap_direction = true;
1599                         }
1600
1601                         nframes64_t frame_delta = 0;
1602                         
1603                         bool left_direction = false;
1604                         if (last_pointer_frame() > pf) {
1605                                 left_direction = true;
1606                         }
1607
1608                         if (left_direction) {
1609                                 frame_delta = (last_pointer_frame() - pf);
1610                         } else {
1611                                 frame_delta = (pf - last_pointer_frame());
1612                         }
1613
1614                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1615                                 _editor->single_contents_trim (*i->view, frame_delta, left_direction, swap_direction);
1616                         }
1617                 }
1618                 break;
1619         }
1620
1621         switch (_operation) {
1622         case StartTrim:
1623                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1624                 break;
1625         case EndTrim:
1626                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1627                 break;
1628         case ContentsTrim:
1629                 _editor->show_verbose_time_cursor (pf, 10);
1630                 break;
1631         }
1632 }
1633
1634
1635 void
1636 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1637 {
1638         if (movement_occurred) {
1639                 motion (event, false);
1640
1641                 if (!_editor->selection->selected (_primary)) {
1642                         _editor->thaw_region_after_trim (*_primary);
1643                 } else {
1644
1645                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1646                                 _editor->thaw_region_after_trim (*i->view);
1647                                 i->view->enable_display (true);
1648                                 i->view->fake_set_opaque (true);
1649                                 if (_have_transaction) {
1650                                         _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1651                                 }
1652                         }
1653                 }
1654                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1655                         (*p)->thaw ();
1656                 }
1657
1658                 _editor->motion_frozen_playlists.clear ();
1659
1660                 if (_have_transaction) {
1661                         _editor->commit_reversible_command();
1662                 }
1663
1664         } else {
1665                 /* no mouse movement */
1666                 _editor->point_trim (event, adjusted_current_frame (event));
1667         }
1668 }
1669
1670 void
1671 TrimDrag::aborted ()
1672 {
1673         /* Our motion method is changing model state, so use the Undo system
1674            to cancel.  Perhaps not ideal, as this will leave an Undo point
1675            behind which may be slightly odd from the user's point of view.
1676         */
1677
1678         finished (0, true);
1679         
1680         if (_have_transaction) {
1681                 _editor->undo ();
1682         }
1683 }
1684
1685 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1686         : Drag (e, i),
1687           _copy (c)
1688 {
1689         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1690         assert (_marker);
1691 }
1692
1693 void
1694 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1695 {
1696         if (_copy) {
1697                 // create a dummy marker for visual representation of moving the copy.
1698                 // The actual copying is not done before we reach the finish callback.
1699                 char name[64];
1700                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1701                 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1702                                                           *new MeterSection (_marker->meter()));
1703
1704                 _item = &new_marker->the_item ();
1705                 _marker = new_marker;
1706
1707         } else {
1708
1709                 MetricSection& section (_marker->meter());
1710
1711                 if (!section.movable()) {
1712                         return;
1713                 }
1714
1715         }
1716
1717         Drag::start_grab (event, cursor);
1718
1719         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1720
1721         _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1722 }
1723
1724 void
1725 MeterMarkerDrag::motion (GdkEvent* event, bool)
1726 {
1727         nframes64_t const pf = adjusted_current_frame (event);
1728
1729         _marker->set_position (pf);
1730         
1731         _editor->show_verbose_time_cursor (pf, 10);
1732 }
1733
1734 void
1735 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1736 {
1737         if (!movement_occurred) {
1738                 return;
1739         }
1740
1741         motion (event, false);
1742
1743         BBT_Time when;
1744
1745         TempoMap& map (_editor->session()->tempo_map());
1746         map.bbt_time (last_pointer_frame(), when);
1747
1748         if (_copy == true) {
1749                 _editor->begin_reversible_command (_("copy meter mark"));
1750                 XMLNode &before = map.get_state();
1751                 map.add_meter (_marker->meter(), when);
1752                 XMLNode &after = map.get_state();
1753                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1754                 _editor->commit_reversible_command ();
1755
1756                 // delete the dummy marker we used for visual representation of copying.
1757                 // a new visual marker will show up automatically.
1758                 delete _marker;
1759         } else {
1760                 _editor->begin_reversible_command (_("move meter mark"));
1761                 XMLNode &before = map.get_state();
1762                 map.move_meter (_marker->meter(), when);
1763                 XMLNode &after = map.get_state();
1764                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1765                 _editor->commit_reversible_command ();
1766         }
1767 }
1768
1769 void
1770 MeterMarkerDrag::aborted ()
1771 {
1772         _marker->set_position (_marker->meter().frame ());
1773 }
1774
1775 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1776         : Drag (e, i),
1777           _copy (c)
1778 {
1779         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1780         assert (_marker);
1781 }
1782
1783 void
1784 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1785 {
1786
1787         if (_copy) {
1788
1789                 // create a dummy marker for visual representation of moving the copy.
1790                 // The actual copying is not done before we reach the finish callback.
1791                 char name[64];
1792                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1793                 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1794                                                           *new TempoSection (_marker->tempo()));
1795
1796                 _item = &new_marker->the_item ();
1797                 _marker = new_marker;
1798
1799         } else {
1800
1801                 MetricSection& section (_marker->tempo());
1802
1803                 if (!section.movable()) {
1804                         return;
1805                 }
1806         }
1807
1808         Drag::start_grab (event, cursor);
1809
1810         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1811         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1812 }
1813
1814 void
1815 TempoMarkerDrag::motion (GdkEvent* event, bool)
1816 {
1817         nframes64_t const pf = adjusted_current_frame (event);
1818         _marker->set_position (pf);
1819         _editor->show_verbose_time_cursor (pf, 10);
1820 }
1821
1822 void
1823 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1824 {
1825         if (!movement_occurred) {
1826                 return;
1827         }
1828
1829         motion (event, false);
1830
1831         BBT_Time when;
1832
1833         TempoMap& map (_editor->session()->tempo_map());
1834         map.bbt_time (last_pointer_frame(), when);
1835
1836         if (_copy == true) {
1837                 _editor->begin_reversible_command (_("copy tempo mark"));
1838                 XMLNode &before = map.get_state();
1839                 map.add_tempo (_marker->tempo(), when);
1840                 XMLNode &after = map.get_state();
1841                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1842                 _editor->commit_reversible_command ();
1843
1844                 // delete the dummy marker we used for visual representation of copying.
1845                 // a new visual marker will show up automatically.
1846                 delete _marker;
1847         } else {
1848                 _editor->begin_reversible_command (_("move tempo mark"));
1849                 XMLNode &before = map.get_state();
1850                 map.move_tempo (_marker->tempo(), when);
1851                 XMLNode &after = map.get_state();
1852                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1853                 _editor->commit_reversible_command ();
1854         }
1855 }
1856
1857 void
1858 TempoMarkerDrag::aborted ()
1859 {
1860         _marker->set_position (_marker->tempo().frame());
1861 }
1862
1863 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1864         : Drag (e, i),
1865           _stop (s)
1866 {
1867         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1868         assert (_cursor);
1869 }
1870
1871 void
1872 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1873 {
1874         Drag::start_grab (event, c);
1875
1876         if (!_stop) {
1877
1878                 nframes64_t where = _editor->event_frame (event, 0, 0);
1879
1880                 _editor->snap_to_with_modifier (where, event);
1881                 _editor->playhead_cursor->set_position (where);
1882
1883         }
1884
1885         if (_cursor == _editor->playhead_cursor) {
1886                 _editor->_dragging_playhead = true;
1887
1888                 Session* s = _editor->session ();
1889
1890                 if (s) {
1891                         if (_was_rolling && _stop) {
1892                                 s->request_stop ();
1893                         }
1894
1895                         if (s->is_auditioning()) {
1896                                 s->cancel_audition ();
1897                         }
1898
1899                         s->request_suspend_timecode_transmission ();
1900
1901                         if (s->timecode_transmission_suspended ()) {
1902                                 nframes64_t const f = _editor->playhead_cursor->current_frame;
1903                                 s->send_mmc_locate (f);
1904                                 s->send_full_time_code (f);
1905                         }
1906                 }
1907         }
1908
1909         _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1910
1911         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1912 }
1913
1914 void
1915 CursorDrag::motion (GdkEvent* event, bool)
1916 {
1917         nframes64_t const adjusted_frame = adjusted_current_frame (event);
1918
1919         if (adjusted_frame == last_pointer_frame()) {
1920                 return;
1921         }
1922
1923         _cursor->set_position (adjusted_frame);
1924
1925         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1926
1927         Session* s = _editor->session ();
1928         if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
1929                 nframes64_t const f = _editor->playhead_cursor->current_frame;
1930                 s->send_mmc_locate (f);
1931                 s->send_full_time_code (f);
1932         }
1933         
1934
1935 #ifdef GTKOSX
1936         _editor->update_canvas_now ();
1937 #endif
1938         _editor->UpdateAllTransportClocks (_cursor->current_frame);
1939 }
1940
1941 void
1942 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1943 {
1944         _editor->_dragging_playhead = false;
1945
1946         if (!movement_occurred && _stop) {
1947                 return;
1948         }
1949
1950         motion (event, false);
1951
1952         if (_item == &_editor->playhead_cursor->canvas_item) {
1953                 Session* s = _editor->session ();
1954                 if (s) {
1955                         s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1956                         _editor->_pending_locate_request = true;
1957                         s->request_resume_timecode_transmission ();
1958                 }
1959         }
1960 }
1961
1962 void
1963 CursorDrag::aborted ()
1964 {
1965         if (_editor->_dragging_playhead) {
1966                 _editor->session()->request_resume_timecode_transmission ();
1967                 _editor->_dragging_playhead = false;
1968         }
1969         
1970         _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
1971 }
1972
1973 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1974         : RegionDrag (e, i, p, v)
1975 {
1976
1977 }
1978
1979 void
1980 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1981 {
1982         Drag::start_grab (event, cursor);
1983
1984         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
1985         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
1986
1987         _pointer_frame_offset = raw_grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
1988         _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1989         
1990         arv->show_fade_line((nframes64_t) r->fade_in()->back()->when);
1991 }
1992
1993 void
1994 FadeInDrag::motion (GdkEvent* event, bool)
1995 {
1996         nframes64_t fade_length;
1997
1998         nframes64_t const pos = adjusted_current_frame (event);
1999
2000         boost::shared_ptr<Region> region = _primary->region ();
2001
2002         if (pos < (region->position() + 64)) {
2003                 fade_length = 64; // this should be a minimum defined somewhere
2004         } else if (pos > region->last_frame()) {
2005                 fade_length = region->length();
2006         } else {
2007                 fade_length = pos - region->position();
2008         }
2009
2010         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2011
2012                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2013
2014                 if (!tmp) {
2015                         continue;
2016                 }
2017
2018                 tmp->reset_fade_in_shape_width (fade_length);
2019                 tmp->show_fade_line((nframes64_t) fade_length);
2020         }
2021
2022         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2023 }
2024
2025 void
2026 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2027 {
2028         if (!movement_occurred) {
2029                 return;
2030         }
2031
2032         nframes64_t fade_length;
2033
2034         nframes64_t const pos = adjusted_current_frame (event);
2035
2036         boost::shared_ptr<Region> region = _primary->region ();
2037
2038         if (pos < (region->position() + 64)) {
2039                 fade_length = 64; // this should be a minimum defined somewhere
2040         } else if (pos > region->last_frame()) {
2041                 fade_length = region->length();
2042         } else {
2043                 fade_length = pos - region->position();
2044         }
2045
2046         _editor->begin_reversible_command (_("change fade in length"));
2047
2048         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2049
2050                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2051
2052                 if (!tmp) {
2053                         continue;
2054                 }
2055
2056                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2057                 XMLNode &before = alist->get_state();
2058
2059                 tmp->audio_region()->set_fade_in_length (fade_length);
2060                 tmp->audio_region()->set_fade_in_active (true);
2061                 tmp->hide_fade_line();
2062
2063                 XMLNode &after = alist->get_state();
2064                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2065         }
2066
2067         _editor->commit_reversible_command ();
2068 }
2069
2070 void
2071 FadeInDrag::aborted ()
2072 {
2073         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2074                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2075
2076                 if (!tmp) {
2077                         continue;
2078                 }
2079
2080                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2081                 tmp->hide_fade_line();
2082         }
2083 }
2084
2085 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2086         : RegionDrag (e, i, p, v)
2087 {
2088
2089 }
2090
2091 void
2092 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2093 {
2094         Drag::start_grab (event, cursor);
2095
2096         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2097         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2098
2099         _pointer_frame_offset = raw_grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2100         _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2101         
2102         arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2103 }
2104
2105 void
2106 FadeOutDrag::motion (GdkEvent* event, bool)
2107 {
2108         nframes64_t fade_length;
2109
2110         nframes64_t const pos = adjusted_current_frame (event);
2111
2112         boost::shared_ptr<Region> region = _primary->region ();
2113
2114         if (pos > (region->last_frame() - 64)) {
2115                 fade_length = 64; // this should really be a minimum fade defined somewhere
2116         }
2117         else if (pos < region->position()) {
2118                 fade_length = region->length();
2119         }
2120         else {
2121                 fade_length = region->last_frame() - pos;
2122         }
2123
2124         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2125
2126                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2127
2128                 if (!tmp) {
2129                         continue;
2130                 }
2131
2132                 tmp->reset_fade_out_shape_width (fade_length);
2133                 tmp->show_fade_line(region->length() - fade_length);
2134         }
2135
2136         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2137 }
2138
2139 void
2140 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2141 {
2142         if (!movement_occurred) {
2143                 return;
2144         }
2145
2146         nframes64_t fade_length;
2147
2148         nframes64_t const pos = adjusted_current_frame (event);
2149
2150         boost::shared_ptr<Region> region = _primary->region ();
2151
2152         if (pos > (region->last_frame() - 64)) {
2153                 fade_length = 64; // this should really be a minimum fade defined somewhere
2154         }
2155         else if (pos < region->position()) {
2156                 fade_length = region->length();
2157         }
2158         else {
2159                 fade_length = region->last_frame() - pos;
2160         }
2161
2162         _editor->begin_reversible_command (_("change fade out length"));
2163
2164         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2165
2166                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2167
2168                 if (!tmp) {
2169                         continue;
2170                 }
2171
2172                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2173                 XMLNode &before = alist->get_state();
2174
2175                 tmp->audio_region()->set_fade_out_length (fade_length);
2176                 tmp->audio_region()->set_fade_out_active (true);
2177                 tmp->hide_fade_line();
2178
2179                 XMLNode &after = alist->get_state();
2180                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2181         }
2182
2183         _editor->commit_reversible_command ();
2184 }
2185
2186 void
2187 FadeOutDrag::aborted ()
2188 {
2189         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2190                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2191
2192                 if (!tmp) {
2193                         continue;
2194                 }
2195
2196                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2197                 tmp->hide_fade_line();
2198         }
2199 }
2200
2201 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2202         : Drag (e, i)
2203 {
2204         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2205         assert (_marker);
2206
2207         _points.push_back (Gnome::Art::Point (0, 0));
2208         _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2209
2210         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2211         _line->property_width_pixels() = 1;
2212         _line->property_points () = _points;
2213         _line->hide ();
2214
2215         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2216 }
2217
2218 MarkerDrag::~MarkerDrag ()
2219 {
2220         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2221                 delete *i;
2222         }
2223 }
2224
2225 void
2226 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2227 {
2228         Drag::start_grab (event, cursor);
2229
2230         bool is_start;
2231
2232         Location *location = _editor->find_location_from_marker (_marker, is_start);
2233         _editor->_dragging_edit_point = true;
2234
2235         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2236
2237         update_item (location);
2238
2239         // _drag_line->show();
2240         // _line->raise_to_top();
2241
2242         if (is_start) {
2243                 _editor->show_verbose_time_cursor (location->start(), 10);
2244         } else {
2245                 _editor->show_verbose_time_cursor (location->end(), 10);
2246         }
2247
2248         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2249
2250         switch (op) {
2251         case Selection::Toggle:
2252                 _editor->selection->toggle (_marker);
2253                 break;
2254         case Selection::Set:
2255                 if (!_editor->selection->selected (_marker)) {
2256                         _editor->selection->set (_marker);
2257                 }
2258                 break;
2259         case Selection::Extend:
2260         {
2261                 Locations::LocationList ll;
2262                 list<Marker*> to_add;
2263                 nframes64_t s, e;
2264                 _editor->selection->markers.range (s, e);
2265                 s = min (_marker->position(), s);
2266                 e = max (_marker->position(), e);
2267                 s = min (s, e);
2268                 e = max (s, e);
2269                 if (e < max_frames) {
2270                         ++e;
2271                 }
2272                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2273                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2274                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2275                         if (lm) {
2276                                 if (lm->start) {
2277                                         to_add.push_back (lm->start);
2278                                 }
2279                                 if (lm->end) {
2280                                         to_add.push_back (lm->end);
2281                                 }
2282                         }
2283                 }
2284                 if (!to_add.empty()) {
2285                         _editor->selection->add (to_add);
2286                 }
2287                 break;
2288         }
2289         case Selection::Add:
2290                 _editor->selection->add (_marker);
2291                 break;
2292         }
2293
2294         /* Set up copies for us to manipulate during the drag */
2295
2296         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2297                 Location* l = _editor->find_location_from_marker (*i, is_start);
2298                 _copied_locations.push_back (new Location (*l));
2299         }
2300 }
2301
2302 void
2303 MarkerDrag::motion (GdkEvent* event, bool)
2304 {
2305         nframes64_t f_delta = 0;
2306         bool is_start;
2307         bool move_both = false;
2308         Marker* marker;
2309         Location *real_location;
2310         Location *copy_location = 0;
2311
2312         nframes64_t const newframe = adjusted_current_frame (event);
2313
2314         nframes64_t next = newframe;
2315
2316         if (newframe == last_pointer_frame()) {
2317                 return;
2318         }
2319
2320         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2321                 move_both = true;
2322         }
2323
2324         MarkerSelection::iterator i;
2325         list<Location*>::iterator x;
2326
2327         /* find the marker we're dragging, and compute the delta */
2328
2329         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2330              x != _copied_locations.end() && i != _editor->selection->markers.end();
2331              ++i, ++x) {
2332
2333                 copy_location = *x;
2334                 marker = *i;
2335
2336                 if (marker == _marker) {
2337
2338                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2339                                 /* que pasa ?? */
2340                                 return;
2341                         }
2342
2343                         if (real_location->is_mark()) {
2344                                 f_delta = newframe - copy_location->start();
2345                         } else {
2346
2347
2348                                 switch (marker->type()) {
2349                                 case Marker::Start:
2350                                 case Marker::LoopStart:
2351                                 case Marker::PunchIn:
2352                                         f_delta = newframe - copy_location->start();
2353                                         break;
2354
2355                                 case Marker::End:
2356                                 case Marker::LoopEnd:
2357                                 case Marker::PunchOut:
2358                                         f_delta = newframe - copy_location->end();
2359                                         break;
2360                                 default:
2361                                         /* what kind of marker is this ? */
2362                                         return;
2363                                 }
2364                         }
2365                         break;
2366                 }
2367         }
2368
2369         if (i == _editor->selection->markers.end()) {
2370                 /* hmm, impossible - we didn't find the dragged marker */
2371                 return;
2372         }
2373
2374         /* now move them all */
2375
2376         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2377              x != _copied_locations.end() && i != _editor->selection->markers.end();
2378              ++i, ++x) {
2379
2380                 copy_location = *x;
2381                 marker = *i;
2382
2383                 /* call this to find out if its the start or end */
2384
2385                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2386                         continue;
2387                 }
2388
2389                 if (real_location->locked()) {
2390                         continue;
2391                 }
2392
2393                 if (copy_location->is_mark()) {
2394
2395                         /* now move it */
2396
2397                         copy_location->set_start (copy_location->start() + f_delta);
2398
2399                 } else {
2400
2401                         nframes64_t new_start = copy_location->start() + f_delta;
2402                         nframes64_t new_end = copy_location->end() + f_delta;
2403
2404                         if (is_start) { // start-of-range marker
2405
2406                                 if (move_both) {
2407                                         copy_location->set_start (new_start);
2408                                         copy_location->set_end (new_end);
2409                                 } else  if (new_start < copy_location->end()) {
2410                                         copy_location->set_start (new_start);
2411                                 } else {
2412                                         _editor->snap_to (next, 1, true);
2413                                         copy_location->set_end (next);
2414                                         copy_location->set_start (newframe);
2415                                 }
2416
2417                         } else { // end marker
2418
2419                                 if (move_both) {
2420                                         copy_location->set_end (new_end);
2421                                         copy_location->set_start (new_start);
2422                                 } else if (new_end > copy_location->start()) {
2423                                         copy_location->set_end (new_end);
2424                                 } else if (newframe > 0) {
2425                                         _editor->snap_to (next, -1, true);
2426                                         copy_location->set_start (next);
2427                                         copy_location->set_end (newframe);
2428                                 }
2429                         }
2430                 }
2431
2432                 update_item (copy_location);
2433
2434                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2435
2436                 if (lm) {
2437                         lm->set_position (copy_location->start(), copy_location->end());
2438                 }
2439         }
2440
2441         assert (!_copied_locations.empty());
2442
2443         _editor->show_verbose_time_cursor (newframe, 10);
2444
2445 #ifdef GTKOSX
2446         _editor->update_canvas_now ();
2447 #endif
2448 }
2449
2450 void
2451 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2452 {
2453         if (!movement_occurred) {
2454
2455                 /* just a click, do nothing but finish
2456                    off the selection process
2457                 */
2458
2459                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2460
2461                 switch (op) {
2462                 case Selection::Set:
2463                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2464                                 _editor->selection->set (_marker);
2465                         }
2466                         break;
2467
2468                 case Selection::Toggle:
2469                 case Selection::Extend:
2470                 case Selection::Add:
2471                         break;
2472                 }
2473
2474                 return;
2475         }
2476
2477         _editor->_dragging_edit_point = false;
2478
2479         _editor->begin_reversible_command ( _("move marker") );
2480         XMLNode &before = _editor->session()->locations()->get_state();
2481
2482         MarkerSelection::iterator i;
2483         list<Location*>::iterator x;
2484         bool is_start;
2485
2486         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2487              x != _copied_locations.end() && i != _editor->selection->markers.end();
2488              ++i, ++x) {
2489
2490                 Location * location = _editor->find_location_from_marker (*i, is_start);
2491
2492                 if (location) {
2493
2494                         if (location->locked()) {
2495                                 return;
2496                         }
2497
2498                         if (location->is_mark()) {
2499                                 location->set_start ((*x)->start());
2500                         } else {
2501                                 location->set ((*x)->start(), (*x)->end());
2502                         }
2503                 }
2504         }
2505
2506         XMLNode &after = _editor->session()->locations()->get_state();
2507         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2508         _editor->commit_reversible_command ();
2509
2510         _line->hide();
2511 }
2512
2513 void
2514 MarkerDrag::aborted ()
2515 {
2516         /* XXX: TODO */
2517 }
2518
2519 void
2520 MarkerDrag::update_item (Location* location)
2521 {
2522         double const x1 = _editor->frame_to_pixel (location->start());
2523
2524         _points.front().set_x(x1);
2525         _points.back().set_x(x1);
2526         _line->property_points() = _points;
2527 }
2528
2529 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2530         : Drag (e, i),
2531           _cumulative_x_drag (0),
2532           _cumulative_y_drag (0)
2533 {
2534         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2535         assert (_point);
2536 }
2537
2538
2539 void
2540 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2541 {
2542         Drag::start_grab (event, _editor->fader_cursor);
2543
2544         // start the grab at the center of the control point so
2545         // the point doesn't 'jump' to the mouse after the first drag
2546         _fixed_grab_x = _point->get_x();
2547         _fixed_grab_y = _point->get_y();
2548
2549         float const fraction = 1 - (_point->get_y() / _point->line().height());
2550
2551         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2552
2553         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2554                                             event->button.x + 10, event->button.y + 10);
2555
2556         _editor->show_verbose_canvas_cursor ();
2557 }
2558
2559 void
2560 ControlPointDrag::motion (GdkEvent* event, bool)
2561 {
2562         double dx = _drags->current_pointer_x() - last_pointer_x();
2563         double dy = _drags->current_pointer_y() - last_pointer_y();
2564
2565         if (event->button.state & Keyboard::SecondaryModifier) {
2566                 dx *= 0.1;
2567                 dy *= 0.1;
2568         }
2569
2570         /* coordinate in pixels relative to the start of the region (for region-based automation)
2571            or track (for track-based automation) */
2572         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2573         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2574
2575         // calculate zero crossing point. back off by .01 to stay on the
2576         // positive side of zero
2577         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2578
2579         // make sure we hit zero when passing through
2580         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2581                 cy = zero_gain_y;
2582         }
2583
2584         if (_x_constrained) {
2585                 cx = _fixed_grab_x;
2586         }
2587         if (_y_constrained) {
2588                 cy = _fixed_grab_y;
2589         }
2590
2591         _cumulative_x_drag = cx - _fixed_grab_x;
2592         _cumulative_y_drag = cy - _fixed_grab_y;
2593
2594         cx = max (0.0, cx);
2595         cy = max (0.0, cy);
2596         cy = min ((double) _point->line().height(), cy);
2597
2598         framepos_t cx_frames = _editor->unit_to_frame (cx);
2599         
2600         if (!_x_constrained) {
2601                 _editor->snap_to_with_modifier (cx_frames, event);
2602         }
2603
2604         cx_frames = min (cx_frames, _point->line().maximum_time());
2605
2606         float const fraction = 1.0 - (cy / _point->line().height());
2607
2608         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2609
2610         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2611
2612         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2613 }
2614
2615 void
2616 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2617 {
2618         if (!movement_occurred) {
2619
2620                 /* just a click */
2621
2622                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2623                         _editor->reset_point_selection ();
2624                 }
2625
2626         } else {
2627                 motion (event, false);
2628         }
2629         _point->line().end_drag ();
2630 }
2631
2632 void
2633 ControlPointDrag::aborted ()
2634 {
2635         _point->line().reset ();
2636 }
2637
2638 bool
2639 ControlPointDrag::active (Editing::MouseMode m)
2640 {
2641         if (m == Editing::MouseGain) {
2642                 /* always active in mouse gain */
2643                 return true;
2644         }
2645
2646         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2647         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2648 }
2649
2650 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2651         : Drag (e, i),
2652           _line (0),
2653           _cumulative_y_drag (0)
2654 {
2655
2656 }
2657 void
2658 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2659 {
2660         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2661         assert (_line);
2662
2663         _item = &_line->grab_item ();
2664
2665         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2666            origin, and ditto for y.
2667         */
2668
2669         double cx = event->button.x;
2670         double cy = event->button.y;
2671
2672         _line->parent_group().w2i (cx, cy);
2673
2674         nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2675
2676         uint32_t before;
2677         uint32_t after;
2678         
2679         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2680                 /* no adjacent points */
2681                 return;
2682         }
2683
2684         Drag::start_grab (event, _editor->fader_cursor);
2685
2686         /* store grab start in parent frame */
2687
2688         _fixed_grab_x = cx;
2689         _fixed_grab_y = cy;
2690
2691         double fraction = 1.0 - (cy / _line->height());
2692
2693         _line->start_drag_line (before, after, fraction);
2694
2695         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2696                                             event->button.x + 10, event->button.y + 10);
2697
2698         _editor->show_verbose_canvas_cursor ();
2699 }
2700
2701 void
2702 LineDrag::motion (GdkEvent* event, bool)
2703 {
2704         double dy = _drags->current_pointer_y() - last_pointer_y();
2705
2706         if (event->button.state & Keyboard::SecondaryModifier) {
2707                 dy *= 0.1;
2708         }
2709
2710         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2711
2712         _cumulative_y_drag = cy - _fixed_grab_y;
2713
2714         cy = max (0.0, cy);
2715         cy = min ((double) _line->height(), cy);
2716
2717         double const fraction = 1.0 - (cy / _line->height());
2718
2719         bool push;
2720
2721         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2722                 push = false;
2723         } else {
2724                 push = true;
2725         }
2726
2727         /* we are ignoring x position for this drag, so we can just pass in anything */
2728         _line->drag_motion (0, fraction, true, push);
2729
2730         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2731 }
2732
2733 void
2734 LineDrag::finished (GdkEvent* event, bool)
2735 {
2736         motion (event, false);
2737         _line->end_drag ();
2738 }
2739
2740 void
2741 LineDrag::aborted ()
2742 {
2743         _line->reset ();
2744 }
2745
2746 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2747         : Drag (e, i),
2748           _line (0),
2749           _cumulative_x_drag (0)
2750 {
2751
2752 }
2753 void
2754 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2755 {
2756         Drag::start_grab (event);
2757         
2758         _line = reinterpret_cast<SimpleLine*> (_item);
2759         assert (_line);
2760
2761         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2762
2763         double cx = event->button.x;
2764         double cy = event->button.y;
2765
2766         _item->property_parent().get_value()->w2i(cx, cy);
2767
2768         /* store grab start in parent frame */
2769         _region_view_grab_x = cx;
2770         
2771         _before = _line->property_x1();
2772         
2773         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2774                 
2775         _max_x = _editor->frame_to_pixel(_arv->get_duration());
2776 }
2777
2778 void
2779 FeatureLineDrag::motion (GdkEvent* event, bool)
2780 {
2781         double dx = _drags->current_pointer_x() - last_pointer_x();
2782         
2783         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2784         
2785         _cumulative_x_drag += dx;
2786                 
2787         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2788         
2789         if (cx > _max_x){
2790                 cx = _max_x;
2791         }
2792         else if(cx < 0){
2793                 cx = 0;
2794         }
2795         
2796         _line->property_x1() = cx; 
2797         _line->property_x2() = cx;
2798
2799         _before = _line->property_x1();
2800 }
2801
2802 void
2803 FeatureLineDrag::finished (GdkEvent* event, bool)
2804 {
2805         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2806         _arv->update_transient(_before, _line->property_x1());
2807 }
2808
2809 void
2810 FeatureLineDrag::aborted ()
2811 {
2812         //_line->reset ();
2813 }
2814
2815 void
2816 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2817 {
2818         Drag::start_grab (event);
2819         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2820 }
2821
2822 void
2823 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2824 {
2825         nframes64_t start;
2826         nframes64_t end;
2827         double y1;
2828         double y2;
2829
2830         nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2831
2832         nframes64_t grab = grab_frame ();
2833         if (Config->get_rubberbanding_snaps_to_grid ()) {
2834                 _editor->snap_to_with_modifier (grab, event);
2835         }
2836
2837         /* base start and end on initial click position */
2838
2839         if (pf < grab) {
2840                 start = pf;
2841                 end = grab;
2842         } else {
2843                 end = pf;
2844                 start = grab;
2845         }
2846
2847         if (_drags->current_pointer_y() < grab_y()) {
2848                 y1 = _drags->current_pointer_y();
2849                 y2 = grab_y();
2850         } else {
2851                 y2 = _drags->current_pointer_y();
2852                 y1 = grab_y();
2853         }
2854
2855
2856         if (start != end || y1 != y2) {
2857
2858                 double x1 = _editor->frame_to_pixel (start);
2859                 double x2 = _editor->frame_to_pixel (end);
2860
2861                 _editor->rubberband_rect->property_x1() = x1;
2862                 _editor->rubberband_rect->property_y1() = y1;
2863                 _editor->rubberband_rect->property_x2() = x2;
2864                 _editor->rubberband_rect->property_y2() = y2;
2865
2866                 _editor->rubberband_rect->show();
2867                 _editor->rubberband_rect->raise_to_top();
2868
2869                 _editor->show_verbose_time_cursor (pf, 10);
2870         }
2871 }
2872
2873 void
2874 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2875 {
2876         if (movement_occurred) {
2877
2878                 motion (event, false);
2879
2880                 double y1,y2;
2881                 if (_drags->current_pointer_y() < grab_y()) {
2882                         y1 = _drags->current_pointer_y();
2883                         y2 = grab_y();
2884                 } else {
2885                         y2 = _drags->current_pointer_y();
2886                         y1 = grab_y();
2887                 }
2888
2889
2890                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2891                 bool committed;
2892
2893                 _editor->begin_reversible_command (_("rubberband selection"));
2894
2895                 if (grab_frame() < last_pointer_frame()) {
2896                         committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
2897                 } else {
2898                         committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
2899                 }
2900
2901                 if (!committed) {
2902                         _editor->commit_reversible_command ();
2903                 }
2904
2905         } else {
2906                 if (!getenv("ARDOUR_SAE")) {
2907                         _editor->selection->clear_tracks();
2908                 }
2909                 _editor->selection->clear_regions();
2910                 _editor->selection->clear_points ();
2911                 _editor->selection->clear_lines ();
2912         }
2913
2914         _editor->rubberband_rect->hide();
2915 }
2916
2917 void
2918 RubberbandSelectDrag::aborted ()
2919 {
2920         _editor->rubberband_rect->hide ();
2921 }
2922
2923 void
2924 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2925 {
2926         Drag::start_grab (event, cursor);
2927
2928         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2929 }
2930
2931 void
2932 TimeFXDrag::motion (GdkEvent* event, bool)
2933 {
2934         RegionView* rv = _primary;
2935
2936         nframes64_t const pf = adjusted_current_frame (event);
2937
2938         if (pf > rv->region()->position()) {
2939                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2940         }
2941
2942         _editor->show_verbose_time_cursor (pf, 10);
2943 }
2944
2945 void
2946 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2947 {
2948         _primary->get_time_axis_view().hide_timestretch ();
2949
2950         if (!movement_occurred) {
2951                 return;
2952         }
2953
2954         if (last_pointer_frame() < _primary->region()->position()) {
2955                 /* backwards drag of the left edge - not usable */
2956                 return;
2957         }
2958
2959         nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2960
2961         float percentage = (double) newlen / (double) _primary->region()->length();
2962
2963 #ifndef USE_RUBBERBAND
2964         // Soundtouch uses percentage / 100 instead of normal (/ 1)
2965         if (_primary->region()->data_type() == DataType::AUDIO) {
2966                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2967         }
2968 #endif
2969
2970         _editor->begin_reversible_command (_("timestretch"));
2971
2972         // XXX how do timeFX on multiple regions ?
2973
2974         RegionSelection rs;
2975         rs.add (_primary);
2976
2977         if (_editor->time_stretch (rs, percentage) == -1) {
2978                 error << _("An error occurred while executing time stretch operation") << endmsg;
2979         }
2980 }
2981
2982 void
2983 TimeFXDrag::aborted ()
2984 {
2985         _primary->get_time_axis_view().hide_timestretch ();
2986 }
2987
2988
2989 void
2990 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2991 {
2992         Drag::start_grab (event);
2993 }
2994
2995 void
2996 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2997 {
2998         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
2999 }
3000
3001 void
3002 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3003 {
3004         if (movement_occurred && _editor->session()) {
3005                 /* make sure we stop */
3006                 _editor->session()->request_transport_speed (0.0);
3007         }
3008 }
3009
3010 void
3011 ScrubDrag::aborted ()
3012 {
3013         /* XXX: TODO */
3014 }
3015
3016 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3017         : Drag (e, i)
3018         , _operation (o)
3019         , _copy (false)
3020         , _original_pointer_time_axis (-1)
3021         , _last_pointer_time_axis (-1)
3022 {
3023
3024 }
3025
3026 void
3027 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3028 {
3029         nframes64_t start = 0;
3030         nframes64_t end = 0;
3031
3032         if (_editor->session() == 0) {
3033                 return;
3034         }
3035
3036         Gdk::Cursor* cursor = 0;
3037
3038         switch (_operation) {
3039         case CreateSelection:
3040                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3041                         _copy = true;
3042                 } else {
3043                         _copy = false;
3044                 }
3045                 cursor = _editor->selector_cursor;
3046                 Drag::start_grab (event, cursor);
3047                 break;
3048
3049         case SelectionStartTrim:
3050                 if (_editor->clicked_axisview) {
3051                         _editor->clicked_axisview->order_selection_trims (_item, true);
3052                 }
3053                 Drag::start_grab (event, _editor->left_side_trim_cursor);
3054                 start = _editor->selection->time[_editor->clicked_selection].start;
3055                 _pointer_frame_offset = raw_grab_frame() - start;
3056                 break;
3057
3058         case SelectionEndTrim:
3059                 if (_editor->clicked_axisview) {
3060                         _editor->clicked_axisview->order_selection_trims (_item, false);
3061                 }
3062                 Drag::start_grab (event, _editor->right_side_trim_cursor);
3063                 end = _editor->selection->time[_editor->clicked_selection].end;
3064                 _pointer_frame_offset = raw_grab_frame() - end;
3065                 break;
3066
3067         case SelectionMove:
3068                 start = _editor->selection->time[_editor->clicked_selection].start;
3069                 Drag::start_grab (event, cursor);
3070                 _pointer_frame_offset = raw_grab_frame() - start;
3071                 break;
3072         }
3073
3074         if (_operation == SelectionMove) {
3075                 _editor->show_verbose_time_cursor (start, 10);
3076         } else {
3077                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3078         }
3079
3080         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3081 }
3082
3083 void
3084 SelectionDrag::motion (GdkEvent* event, bool first_move)
3085 {
3086         nframes64_t start = 0;
3087         nframes64_t end = 0;
3088         nframes64_t length;
3089
3090         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3091         if (pending_time_axis.first == 0) {
3092                 return;
3093         }
3094         
3095         nframes64_t const pending_position = adjusted_current_frame (event);
3096
3097         /* only alter selection if things have changed */
3098
3099         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3100                 return;
3101         }
3102
3103         switch (_operation) {
3104         case CreateSelection:
3105         {
3106                 nframes64_t grab = grab_frame ();
3107
3108                 if (first_move) {
3109                         _editor->snap_to (grab);
3110                 }
3111
3112                 if (pending_position < grab_frame()) {
3113                         start = pending_position;
3114                         end = grab;
3115                 } else {
3116                         end = pending_position;
3117                         start = grab;
3118                 }
3119
3120                 /* first drag: Either add to the selection
3121                    or create a new selection
3122                 */
3123
3124                 if (first_move) {
3125
3126                         if (_copy) {
3127                                 /* adding to the selection */
3128                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3129                                 //_editor->selection->add (_editor->clicked_axisview);
3130                                 _editor->clicked_selection = _editor->selection->add (start, end);
3131                                 _copy = false;
3132                         } else {
3133                                 /* new selection */
3134
3135                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3136                                         //_editor->selection->set (_editor->clicked_axisview);
3137                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3138                                 }
3139                                 
3140                                 _editor->clicked_selection = _editor->selection->set (start, end);
3141                         }
3142                 }
3143
3144                 /* select the track that we're in */
3145                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3146                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3147                         _editor->selection->add (pending_time_axis.first);
3148                         _added_time_axes.push_back (pending_time_axis.first);
3149                 }
3150
3151                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3152                    tracks that we selected in the first place.
3153                 */
3154                 
3155                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3156                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3157
3158                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3159                 while (i != _added_time_axes.end()) {
3160
3161                         list<TimeAxisView*>::iterator tmp = i;
3162                         ++tmp;
3163                         
3164                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3165                                 _editor->selection->remove (*i);
3166                                 _added_time_axes.remove (*i);
3167                         }
3168
3169                         i = tmp;
3170                 }
3171
3172         }
3173         break;
3174
3175         case SelectionStartTrim:
3176
3177                 start = _editor->selection->time[_editor->clicked_selection].start;
3178                 end = _editor->selection->time[_editor->clicked_selection].end;
3179
3180                 if (pending_position > end) {
3181                         start = end;
3182                 } else {
3183                         start = pending_position;
3184                 }
3185                 break;
3186
3187         case SelectionEndTrim:
3188
3189                 start = _editor->selection->time[_editor->clicked_selection].start;
3190                 end = _editor->selection->time[_editor->clicked_selection].end;
3191
3192                 if (pending_position < start) {
3193                         end = start;
3194                 } else {
3195                         end = pending_position;
3196                 }
3197
3198                 break;
3199
3200         case SelectionMove:
3201
3202                 start = _editor->selection->time[_editor->clicked_selection].start;
3203                 end = _editor->selection->time[_editor->clicked_selection].end;
3204
3205                 length = end - start;
3206
3207                 start = pending_position;
3208                 _editor->snap_to (start);
3209
3210                 end = start + length;
3211
3212                 break;
3213         }
3214
3215         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3216                 _editor->start_canvas_autoscroll (1, 0);
3217         }
3218
3219         if (start != end) {
3220                 _editor->selection->replace (_editor->clicked_selection, start, end);
3221         }
3222
3223         if (_operation == SelectionMove) {
3224                 _editor->show_verbose_time_cursor(start, 10);
3225         } else {
3226                 _editor->show_verbose_time_cursor(pending_position, 10);
3227         }
3228 }
3229
3230 void
3231 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3232 {
3233         Session* s = _editor->session();
3234
3235         if (movement_occurred) {
3236                 motion (event, false);
3237                 /* XXX this is not object-oriented programming at all. ick */
3238                 if (_editor->selection->time.consolidate()) {
3239                         _editor->selection->TimeChanged ();
3240                 }
3241
3242                 /* XXX what if its a music time selection? */
3243                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3244                         s->request_play_range (&_editor->selection->time, true);
3245                 }
3246
3247
3248         } else {
3249                 /* just a click, no pointer movement.*/
3250
3251                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3252                         _editor->selection->clear_time();
3253                 }
3254
3255                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3256                         _editor->selection->set (_editor->clicked_axisview);
3257                 }
3258                 
3259                 if (s && s->get_play_range () && s->transport_rolling()) {
3260                         s->request_stop (false, false);
3261                 }
3262
3263         }
3264
3265         _editor->stop_canvas_autoscroll ();
3266 }
3267
3268 void
3269 SelectionDrag::aborted ()
3270 {
3271         /* XXX: TODO */
3272 }
3273
3274 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3275         : Drag (e, i),
3276           _operation (o),
3277           _copy (false)
3278 {
3279         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, 
3280                                                    physical_screen_height (_editor->get_window()));
3281         _drag_rect->hide ();
3282
3283         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3284         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3285 }
3286
3287 void
3288 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3289 {
3290         if (_editor->session() == 0) {
3291                 return;
3292         }
3293
3294         Gdk::Cursor* cursor = 0;
3295
3296         if (!_editor->temp_location) {
3297                 _editor->temp_location = new Location (*_editor->session());
3298         }
3299
3300         switch (_operation) {
3301         case CreateRangeMarker:
3302         case CreateTransportMarker:
3303         case CreateCDMarker:
3304
3305                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3306                         _copy = true;
3307                 } else {
3308                         _copy = false;
3309                 }
3310                 cursor = _editor->selector_cursor;
3311                 break;
3312         }
3313
3314         Drag::start_grab (event, cursor);
3315
3316         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3317 }
3318
3319 void
3320 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3321 {
3322         nframes64_t start = 0;
3323         nframes64_t end = 0;
3324         ArdourCanvas::SimpleRect *crect;
3325
3326         switch (_operation) {
3327         case CreateRangeMarker:
3328                 crect = _editor->range_bar_drag_rect;
3329                 break;
3330         case CreateTransportMarker:
3331                 crect = _editor->transport_bar_drag_rect;
3332                 break;
3333         case CreateCDMarker:
3334                 crect = _editor->cd_marker_bar_drag_rect;
3335                 break;
3336         default:
3337                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3338                 return;
3339                 break;
3340         }
3341
3342         nframes64_t const pf = adjusted_current_frame (event);
3343
3344         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3345                 nframes64_t grab = grab_frame ();
3346                 _editor->snap_to (grab);
3347                 
3348                 if (pf < grab_frame()) {
3349                         start = pf;
3350                         end = grab;
3351                 } else {
3352                         end = pf;
3353                         start = grab;
3354                 }
3355
3356                 /* first drag: Either add to the selection
3357                    or create a new selection.
3358                 */
3359
3360                 if (first_move) {
3361
3362                         _editor->temp_location->set (start, end);
3363
3364                         crect->show ();
3365
3366                         update_item (_editor->temp_location);
3367                         _drag_rect->show();
3368                         //_drag_rect->raise_to_top();
3369
3370                 }
3371         }
3372
3373         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3374                 _editor->start_canvas_autoscroll (1, 0);
3375         }
3376
3377         if (start != end) {
3378                 _editor->temp_location->set (start, end);
3379
3380                 double x1 = _editor->frame_to_pixel (start);
3381                 double x2 = _editor->frame_to_pixel (end);
3382                 crect->property_x1() = x1;
3383                 crect->property_x2() = x2;
3384
3385                 update_item (_editor->temp_location);
3386         }
3387
3388         _editor->show_verbose_time_cursor (pf, 10);
3389
3390 }
3391
3392 void
3393 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3394 {
3395         Location * newloc = 0;
3396         string rangename;
3397         int flags;
3398
3399         if (movement_occurred) {
3400                 motion (event, false);
3401                 _drag_rect->hide();
3402
3403                 switch (_operation) {
3404                 case CreateRangeMarker:
3405                 case CreateCDMarker:
3406                     {
3407                         _editor->begin_reversible_command (_("new range marker"));
3408                         XMLNode &before = _editor->session()->locations()->get_state();
3409                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3410                         if (_operation == CreateCDMarker) {
3411                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3412                                 _editor->cd_marker_bar_drag_rect->hide();
3413                         }
3414                         else {
3415                                 flags = Location::IsRangeMarker;
3416                                 _editor->range_bar_drag_rect->hide();
3417                         }
3418                         newloc = new Location (
3419                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3420                                 );
3421                         
3422                         _editor->session()->locations()->add (newloc, true);
3423                         XMLNode &after = _editor->session()->locations()->get_state();
3424                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3425                         _editor->commit_reversible_command ();
3426                         break;
3427                     }
3428
3429                 case CreateTransportMarker:
3430                         // popup menu to pick loop or punch
3431                         _editor->new_transport_marker_context_menu (&event->button, _item);
3432                         break;
3433                 }
3434         } else {
3435                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3436
3437                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3438
3439                         nframes64_t start;
3440                         nframes64_t end;
3441
3442                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3443
3444                         if (end == max_frames) {
3445                                 end = _editor->session()->current_end_frame ();
3446                         }
3447
3448                         if (start == max_frames) {
3449                                 start = _editor->session()->current_start_frame ();
3450                         }
3451
3452                         switch (_editor->mouse_mode) {
3453                         case MouseObject:
3454                                 /* find the two markers on either side and then make the selection from it */
3455                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3456                                 break;
3457
3458                         case MouseRange:
3459                                 /* find the two markers on either side of the click and make the range out of it */
3460                                 _editor->selection->set (start, end);
3461                                 break;
3462
3463                         default:
3464                                 break;
3465                         }
3466                 }
3467         }
3468
3469         _editor->stop_canvas_autoscroll ();
3470 }
3471
3472 void
3473 RangeMarkerBarDrag::aborted ()
3474 {
3475         /* XXX: TODO */
3476 }
3477
3478 void
3479 RangeMarkerBarDrag::update_item (Location* location)
3480 {
3481         double const x1 = _editor->frame_to_pixel (location->start());
3482         double const x2 = _editor->frame_to_pixel (location->end());
3483
3484         _drag_rect->property_x1() = x1;
3485         _drag_rect->property_x2() = x2;
3486 }
3487
3488 void
3489 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3490 {
3491         Drag::start_grab (event, _editor->zoom_cursor);
3492         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3493 }
3494
3495 void
3496 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3497 {
3498         nframes64_t start;
3499         nframes64_t end;
3500
3501         nframes64_t const pf = adjusted_current_frame (event);
3502
3503         nframes64_t grab = grab_frame ();
3504         _editor->snap_to_with_modifier (grab, event);
3505
3506         /* base start and end on initial click position */
3507         if (pf < grab) {
3508                 start = pf;
3509                 end = grab;
3510         } else {
3511                 end = pf;
3512                 start = grab;
3513         }
3514
3515         if (start != end) {
3516
3517                 if (first_move) {
3518                         _editor->zoom_rect->show();
3519                         _editor->zoom_rect->raise_to_top();
3520                 }
3521
3522                 _editor->reposition_zoom_rect(start, end);
3523
3524                 _editor->show_verbose_time_cursor (pf, 10);
3525         }
3526 }
3527
3528 void
3529 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3530 {
3531         if (movement_occurred) {
3532                 motion (event, false);
3533
3534                 if (grab_frame() < last_pointer_frame()) {
3535                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3536                 } else {
3537                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3538                 }
3539         } else {
3540                 _editor->temporal_zoom_to_frame (false, grab_frame());
3541                 /*
3542                 temporal_zoom_step (false);
3543                 center_screen (grab_frame());
3544                 */
3545         }
3546
3547         _editor->zoom_rect->hide();
3548 }
3549
3550 void
3551 MouseZoomDrag::aborted ()
3552 {
3553         _editor->zoom_rect->hide ();
3554 }
3555
3556 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3557         : Drag (e, i)
3558         , _cumulative_dx (0)
3559         , _cumulative_dy (0)
3560 {
3561         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3562         _region = &_primary->region_view ();
3563         _note_height = _region->midi_stream_view()->note_height ();
3564 }
3565
3566 void
3567 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3568 {
3569         Drag::start_grab (event);
3570
3571         if (!(_was_selected = _primary->selected())) {
3572
3573                 /* tertiary-click means extend selection - we'll do that on button release,
3574                    so don't add it here, because otherwise we make it hard to figure
3575                    out the "extend-to" range.
3576                 */
3577
3578                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3579
3580                 if (!extend) {
3581                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3582
3583                         if (add) {
3584                                 _region->note_selected (_primary, true);
3585                         } else {
3586                                 _region->unique_select (_primary);
3587                         }
3588                 }
3589         }
3590 }
3591
3592 /** @return Current total drag x change in frames */
3593 frameoffset_t
3594 NoteDrag::total_dx () const
3595 {
3596         /* dx in frames */
3597         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3598
3599         /* primary note time */
3600         frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3601         
3602         /* new time of the primary note relative to the region position */
3603         frameoffset_t const st = n + dx;
3604
3605         /* snap and return corresponding delta */
3606         return _region->snap_frame_to_frame (st) - n;
3607 }
3608
3609 /** @return Current total drag y change in notes */
3610 int8_t
3611 NoteDrag::total_dy () const
3612 {
3613         /* this is `backwards' to make increasing note number go in the right direction */
3614         double const dy = _drags->current_pointer_y() - grab_y();
3615
3616         /* dy in notes */
3617         int8_t ndy = 0;
3618
3619         if (abs (dy) >= _note_height) {
3620                 if (dy > 0) {
3621                         ndy = (int8_t) ceil (dy / _note_height / 2.0);
3622                 } else {
3623                         ndy = (int8_t) floor (dy / _note_height / 2.0);
3624                 }
3625         }
3626
3627         return ndy;
3628 }
3629         
3630
3631 void
3632 NoteDrag::motion (GdkEvent *, bool)
3633 {
3634         /* Total change in x and y since the start of the drag */
3635         frameoffset_t const dx = total_dx ();
3636         int8_t const dy = total_dy ();
3637
3638         /* Now work out what we have to do to the note canvas items to set this new drag delta */
3639         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3640         double const tdy = dy * _note_height - _cumulative_dy;
3641
3642         if (tdx || tdy) {
3643                 _region->move_selection (tdx, tdy);
3644                 _cumulative_dx += tdx;
3645                 _cumulative_dy += tdy;
3646
3647                 char buf[12];
3648                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + dy).c_str(),
3649                           (int) floor (_primary->note()->note() + dy));
3650                 
3651                 _editor->show_verbose_canvas_cursor_with (buf);
3652         }
3653 }
3654
3655 void
3656 NoteDrag::finished (GdkEvent* ev, bool moved)
3657 {
3658         if (!moved) {
3659                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3660
3661                         if (_was_selected) {
3662                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3663                                 if (add) {
3664                                         _region->note_deselected (_primary);
3665                                 }
3666                         } else {
3667                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3668                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3669
3670                                 if (!extend && !add && _region->selection_size() > 1) {
3671                                         _region->unique_select (_primary);
3672                                 } else if (extend) {
3673                                         _region->note_selected (_primary, true, true);
3674                                 } else {
3675                                         /* it was added during button press */
3676                                 }
3677                         }
3678                 }
3679         } else {
3680                 _region->note_dropped (_primary, total_dx(), - total_dy());
3681         }
3682 }
3683
3684 void
3685 NoteDrag::aborted ()
3686 {
3687         /* XXX: TODO */
3688 }
3689
3690 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3691         : Drag (e, i)
3692         , _ranges (r)
3693         , _nothing_to_drag (false)
3694 {
3695         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3696         assert (_atav);
3697
3698         _line = _atav->line ();
3699 }
3700
3701 void
3702 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3703 {
3704         Drag::start_grab (event, cursor);
3705
3706         list<ControlPoint*> points;
3707
3708         XMLNode* state = &_line->get_state ();
3709         
3710         if (_ranges.empty()) {
3711                 
3712                 uint32_t const N = _line->npoints ();
3713                 for (uint32_t i = 0; i < N; ++i) {
3714                         points.push_back (_line->nth (i));
3715                 }
3716                 
3717         } else {
3718
3719                 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3720                 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3721
3722                         /* fade into and out of the region that we're dragging;
3723                            64 samples length plucked out of thin air.
3724                         */
3725                         nframes64_t const h = (j->start + j->end) / 2;
3726                         nframes64_t a = j->start + 64;
3727                         if (a > h) {
3728                                 a = h;
3729                         }
3730                         nframes64_t b = j->end - 64;
3731                         if (b < h) {
3732                                 b = h;
3733                         }
3734                         
3735                         the_list->add (j->start, the_list->eval (j->start));
3736                         _line->add_always_in_view (j->start);
3737                         the_list->add (a, the_list->eval (a));
3738                         _line->add_always_in_view (a);
3739                         the_list->add (b, the_list->eval (b));
3740                         _line->add_always_in_view (b);
3741                         the_list->add (j->end, the_list->eval (j->end));
3742                         _line->add_always_in_view (j->end);
3743                 }
3744
3745                 uint32_t const N = _line->npoints ();
3746                 for (uint32_t i = 0; i < N; ++i) {
3747
3748                         ControlPoint* p = _line->nth (i);
3749
3750                         list<AudioRange>::const_iterator j = _ranges.begin ();
3751                         while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3752                                 ++j;
3753                         }
3754
3755                         if (j != _ranges.end()) {
3756                                 points.push_back (p);
3757                         }
3758                 }
3759         }
3760
3761         if (points.empty()) {
3762                 _nothing_to_drag = true;
3763                 return;
3764         }
3765
3766         _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3767 }
3768
3769 void
3770 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3771 {
3772         if (_nothing_to_drag) {
3773                 return;
3774         }
3775         
3776         float const f = 1 - (_drags->current_pointer_y() / _line->height());
3777
3778         /* we are ignoring x position for this drag, so we can just pass in anything */
3779         _line->drag_motion (0, f, true, false);
3780 }
3781
3782 void
3783 AutomationRangeDrag::finished (GdkEvent* event, bool)
3784 {
3785         if (_nothing_to_drag) {
3786                 return;
3787         }
3788         
3789         motion (event, false);
3790         _line->end_drag ();
3791         _line->clear_always_in_view ();
3792 }
3793
3794 void
3795 AutomationRangeDrag::aborted ()
3796 {
3797         _line->clear_always_in_view ();
3798         _line->reset ();
3799 }
3800
3801 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
3802         : view (v)
3803 {
3804         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
3805         layer = v->region()->layer ();
3806         initial_y = v->get_canvas_group()->property_y ();
3807         initial_playlist = v->region()->playlist ();
3808 }