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