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