fix some more incorrect casts to CanvasNote rather than CanvasNoteEvent (fixes crashe...
[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::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_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::CanvasNoteEvent*>(_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::CanvasNoteEvent*>(_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                 /* Move the contents of the region around without changing the region bounds */
1537                 _operation = ContentsTrim;
1538                 Drag::start_grab (event, _editor->cursors()->trimmer);
1539         } else {
1540                 /* These will get overridden for a point trim.*/
1541                 if (pf < (region_start + region_length/2)) {
1542                         /* closer to front */
1543                         _operation = StartTrim;
1544                         Drag::start_grab (event, _editor->cursors()->left_side_trim);
1545                 } else {
1546                         /* closer to end */
1547                         _operation = EndTrim;
1548                         Drag::start_grab (event, _editor->cursors()->right_side_trim);
1549                 }
1550         }
1551
1552         switch (_operation) {
1553         case StartTrim:
1554                 _editor->show_verbose_time_cursor (region_start, 10);
1555                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1556                         i->view->trim_front_starting ();
1557                 }
1558                 break;
1559         case EndTrim:
1560                 _editor->show_verbose_time_cursor (region_end, 10);
1561                 break;
1562         case ContentsTrim:
1563                 _editor->show_verbose_time_cursor (pf, 10);
1564                 break;
1565         }
1566
1567         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1568                 i->view->region()->suspend_property_changes ();
1569         }
1570 }
1571
1572 void
1573 TrimDrag::motion (GdkEvent* event, bool first_move)
1574 {
1575         RegionView* rv = _primary;
1576
1577         double speed = 1.0;
1578         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1579         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1580         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1581
1582         if (tv && tv->is_track()) {
1583                 speed = tv->track()->speed();
1584         }
1585
1586         framecnt_t const dt = adjusted_current_frame (event) - grab_frame ();
1587
1588         if (first_move) {
1589
1590                 string trim_type;
1591
1592                 switch (_operation) {
1593                 case StartTrim:
1594                         trim_type = "Region start trim";
1595                         break;
1596                 case EndTrim:
1597                         trim_type = "Region end trim";
1598                         break;
1599                 case ContentsTrim:
1600                         trim_type = "Region content trim";
1601                         break;
1602                 }
1603
1604                 _editor->begin_reversible_command (trim_type);
1605                 _have_transaction = true;
1606
1607                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1608                         RegionView* rv = i->view;
1609                         rv->fake_set_opaque (false);
1610                         rv->enable_display (false);
1611                         rv->region()->clear_changes ();
1612
1613                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1614
1615                         if (arv) {
1616                                 arv->temporarily_hide_envelope ();
1617                         }
1618
1619                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1620                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1621
1622                         if (insert_result.second) {
1623                                 pl->freeze();
1624                         }
1625                 }
1626         }
1627
1628         bool non_overlap_trim = false;
1629
1630         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1631                 non_overlap_trim = true;
1632         }
1633
1634         switch (_operation) {
1635         case StartTrim:
1636                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1637                         i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1638                 }
1639                 break;
1640
1641         case EndTrim:
1642                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1643                         i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1644                 }
1645                 break;
1646
1647         case ContentsTrim:
1648                 {
1649                         bool swap_direction = false;
1650
1651                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1652                                 swap_direction = true;
1653                         }
1654
1655                         framecnt_t frame_delta = 0;
1656                         
1657                         bool left_direction = false;
1658                         if (last_pointer_frame() > adjusted_current_frame(event)) {
1659                                 left_direction = true;
1660                         }
1661
1662                         if (left_direction) {
1663                                 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1664                         } else {
1665                                 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1666                         }
1667
1668                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1669                                 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1670                         }
1671                 }
1672                 break;
1673         }
1674
1675         switch (_operation) {
1676         case StartTrim:
1677                 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1678                 break;
1679         case EndTrim:
1680                 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1681                 break;
1682         case ContentsTrim:
1683                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1684                 break;
1685         }
1686 }
1687
1688
1689 void
1690 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1691 {
1692         if (movement_occurred) {
1693                 motion (event, false);
1694
1695                 /* This must happen before the region's StatefulDiffCommand is created, as it may
1696                    `correct' (ahem) the region's _start from being negative to being zero.  It
1697                    needs to be zero in the undo record.
1698                 */
1699                 if (_operation == StartTrim) {
1700                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1701                                 i->view->trim_front_ending ();
1702                         }
1703                 }
1704                 
1705                 if (!_editor->selection->selected (_primary)) {
1706                         _primary->thaw_after_trim ();
1707                 } else {
1708
1709                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1710                                 i->view->thaw_after_trim ();
1711                                 i->view->enable_display (true);
1712                                 i->view->fake_set_opaque (true);
1713                                 if (_have_transaction) {
1714                                         _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
1715                                 }
1716                         }
1717                 }
1718                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1719                         (*p)->thaw ();
1720                 }
1721
1722                 _editor->motion_frozen_playlists.clear ();
1723                 if (_have_transaction) {
1724                         _editor->commit_reversible_command();
1725                 }
1726
1727         } else {
1728                 /* no mouse movement */
1729                 _editor->point_trim (event, adjusted_current_frame (event));
1730         }
1731
1732         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1733                 if (_operation == StartTrim) {
1734                         i->view->trim_front_ending ();
1735                 }
1736                 
1737                 i->view->region()->resume_property_changes ();
1738         }
1739 }
1740
1741 void
1742 TrimDrag::aborted ()
1743 {
1744         /* Our motion method is changing model state, so use the Undo system
1745            to cancel.  Perhaps not ideal, as this will leave an Undo point
1746            behind which may be slightly odd from the user's point of view.
1747         */
1748
1749         finished (0, true);
1750         
1751         if (_have_transaction) {
1752                 _editor->undo ();
1753         }
1754
1755         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1756                 i->view->region()->resume_property_changes ();
1757         }
1758 }
1759
1760 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1761         : Drag (e, i),
1762           _copy (c)
1763 {
1764         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1765         
1766         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1767         assert (_marker);
1768 }
1769
1770 void
1771 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1772 {
1773         if (_copy) {
1774                 // create a dummy marker for visual representation of moving the copy.
1775                 // The actual copying is not done before we reach the finish callback.
1776                 char name[64];
1777                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1778
1779                 MeterMarker* new_marker = new MeterMarker (
1780                         *_editor,
1781                         *_editor->meter_group,
1782                         *_editor->cursor_group,
1783                         ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1784                         name,
1785                         *new MeterSection (_marker->meter())
1786                         );
1787
1788                 _item = &new_marker->the_item ();
1789                 _marker = new_marker;
1790
1791         } else {
1792
1793                 MetricSection& section (_marker->meter());
1794
1795                 if (!section.movable()) {
1796                         return;
1797                 }
1798
1799         }
1800
1801         Drag::start_grab (event, cursor);
1802
1803         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1804
1805         _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1806 }
1807
1808 void
1809 MeterMarkerDrag::motion (GdkEvent* event, bool)
1810 {
1811         framepos_t const pf = adjusted_current_frame (event);
1812
1813         _marker->set_position (pf);
1814         
1815         _editor->show_verbose_time_cursor (pf, 10);
1816 }
1817
1818 void
1819 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1820 {
1821         if (!movement_occurred) {
1822                 return;
1823         }
1824
1825         motion (event, false);
1826
1827         Timecode::BBT_Time when;
1828
1829         TempoMap& map (_editor->session()->tempo_map());
1830         map.bbt_time (last_pointer_frame(), when);
1831
1832         if (_copy == true) {
1833                 _editor->begin_reversible_command (_("copy meter mark"));
1834                 XMLNode &before = map.get_state();
1835                 map.add_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                 // delete the dummy marker we used for visual representation of copying.
1841                 // a new visual marker will show up automatically.
1842                 delete _marker;
1843         } else {
1844                 _editor->begin_reversible_command (_("move meter mark"));
1845                 XMLNode &before = map.get_state();
1846                 map.move_meter (_marker->meter(), when);
1847                 XMLNode &after = map.get_state();
1848                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1849                 _editor->commit_reversible_command ();
1850         }
1851 }
1852
1853 void
1854 MeterMarkerDrag::aborted ()
1855 {
1856         _marker->set_position (_marker->meter().frame ());
1857 }
1858
1859 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1860         : Drag (e, i),
1861           _copy (c)
1862 {
1863         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1864         
1865         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1866         assert (_marker);
1867 }
1868
1869 void
1870 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1871 {
1872         if (_copy) {
1873
1874                 // create a dummy marker for visual representation of moving the copy.
1875                 // The actual copying is not done before we reach the finish callback.
1876                 char name[64];
1877                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1878
1879                 TempoMarker* new_marker = new TempoMarker (
1880                         *_editor,
1881                         *_editor->tempo_group,
1882                         *_editor->cursor_group,
1883                         ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1884                         name,
1885                         *new TempoSection (_marker->tempo())
1886                         );
1887
1888                 _item = &new_marker->the_item ();
1889                 _marker = new_marker;
1890
1891         }
1892
1893         Drag::start_grab (event, cursor);
1894
1895         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1896         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1897 }
1898
1899 void
1900 TempoMarkerDrag::motion (GdkEvent* event, bool)
1901 {
1902         framepos_t const pf = adjusted_current_frame (event);
1903         _marker->set_position (pf);
1904         _editor->show_verbose_time_cursor (pf, 10);
1905 }
1906
1907 void
1908 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1909 {
1910         if (!movement_occurred) {
1911                 return;
1912         }
1913
1914         motion (event, false);
1915
1916         Timecode::BBT_Time when;
1917
1918         TempoMap& map (_editor->session()->tempo_map());
1919         map.bbt_time (last_pointer_frame(), when);
1920
1921         if (_copy == true) {
1922                 _editor->begin_reversible_command (_("copy tempo mark"));
1923                 XMLNode &before = map.get_state();
1924                 map.add_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                 // delete the dummy marker we used for visual representation of copying.
1930                 // a new visual marker will show up automatically.
1931                 delete _marker;
1932         } else {
1933                 _editor->begin_reversible_command (_("move tempo mark"));
1934                 XMLNode &before = map.get_state();
1935                 map.move_tempo (_marker->tempo(), when);
1936                 XMLNode &after = map.get_state();
1937                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1938                 _editor->commit_reversible_command ();
1939         }
1940 }
1941
1942 void
1943 TempoMarkerDrag::aborted ()
1944 {
1945         _marker->set_position (_marker->tempo().frame());
1946 }
1947
1948 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1949         : Drag (e, i),
1950           _stop (s)
1951 {
1952         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
1953         
1954         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1955         assert (_cursor);
1956 }
1957
1958 void
1959 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1960 {
1961         Drag::start_grab (event, c);
1962
1963         if (!_stop) {
1964
1965                 framepos_t where = _editor->event_frame (event, 0, 0);
1966
1967                 _editor->snap_to_with_modifier (where, event);
1968                 _editor->playhead_cursor->set_position (where);
1969
1970         }
1971
1972         if (_cursor == _editor->playhead_cursor) {
1973                 _editor->_dragging_playhead = true;
1974
1975                 Session* s = _editor->session ();
1976
1977                 if (s) {
1978                         if (_was_rolling && _stop) {
1979                                 s->request_stop ();
1980                         }
1981
1982                         if (s->is_auditioning()) {
1983                                 s->cancel_audition ();
1984                         }
1985
1986                         s->request_suspend_timecode_transmission ();
1987
1988                         if (s->timecode_transmission_suspended ()) {
1989                                 framepos_t const f = _editor->playhead_cursor->current_frame;
1990                                 s->send_mmc_locate (f);
1991                                 s->send_full_time_code (f);
1992                         }
1993                 }
1994         }
1995
1996         _pointer_frame_offset = raw_grab_frame() - _cursor->current_frame;
1997
1998         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1999 }
2000
2001 void
2002 CursorDrag::motion (GdkEvent* event, bool)
2003 {
2004         framepos_t const adjusted_frame = adjusted_current_frame (event);
2005
2006         if (adjusted_frame == last_pointer_frame()) {
2007                 return;
2008         }
2009
2010         _cursor->set_position (adjusted_frame);
2011
2012         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2013
2014         Session* s = _editor->session ();
2015         if (s && _item == &_editor->playhead_cursor->canvas_item && s->timecode_transmission_suspended ()) {
2016                 framepos_t const f = _editor->playhead_cursor->current_frame;
2017                 s->send_mmc_locate (f);
2018                 s->send_full_time_code (f);
2019         }
2020         
2021
2022 #ifdef GTKOSX
2023         _editor->update_canvas_now ();
2024 #endif
2025         _editor->UpdateAllTransportClocks (_cursor->current_frame);
2026 }
2027
2028 void
2029 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2030 {
2031         _editor->_dragging_playhead = false;
2032
2033         if (!movement_occurred && _stop) {
2034                 return;
2035         }
2036
2037         motion (event, false);
2038
2039         if (_item == &_editor->playhead_cursor->canvas_item) {
2040                 Session* s = _editor->session ();
2041                 if (s) {
2042                         s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2043                         _editor->_pending_locate_request = true;
2044                         s->request_resume_timecode_transmission ();
2045                 }
2046         }
2047 }
2048
2049 void
2050 CursorDrag::aborted ()
2051 {
2052         if (_editor->_dragging_playhead) {
2053                 _editor->session()->request_resume_timecode_transmission ();
2054                 _editor->_dragging_playhead = false;
2055         }
2056         
2057         _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2058 }
2059
2060 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2061         : RegionDrag (e, i, p, v)
2062 {
2063         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2064 }
2065
2066 void
2067 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2068 {
2069         Drag::start_grab (event, cursor);
2070
2071         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2072         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2073
2074         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2075         _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2076         
2077         arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2078 }
2079
2080 void
2081 FadeInDrag::motion (GdkEvent* event, bool)
2082 {
2083         framecnt_t fade_length;
2084
2085         framepos_t const pos = adjusted_current_frame (event);
2086
2087         boost::shared_ptr<Region> region = _primary->region ();
2088
2089         if (pos < (region->position() + 64)) {
2090                 fade_length = 64; // this should be a minimum defined somewhere
2091         } else if (pos > region->last_frame()) {
2092                 fade_length = region->length();
2093         } else {
2094                 fade_length = pos - region->position();
2095         }
2096
2097         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2098
2099                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2100
2101                 if (!tmp) {
2102                         continue;
2103                 }
2104
2105                 tmp->reset_fade_in_shape_width (fade_length);
2106                 tmp->show_fade_line((framecnt_t) fade_length);
2107         }
2108
2109         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2110 }
2111
2112 void
2113 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2114 {
2115         if (!movement_occurred) {
2116                 return;
2117         }
2118
2119         framecnt_t fade_length;
2120
2121         framepos_t const pos = adjusted_current_frame (event);
2122
2123         boost::shared_ptr<Region> region = _primary->region ();
2124
2125         if (pos < (region->position() + 64)) {
2126                 fade_length = 64; // this should be a minimum defined somewhere
2127         } else if (pos > region->last_frame()) {
2128                 fade_length = region->length();
2129         } else {
2130                 fade_length = pos - region->position();
2131         }
2132
2133         _editor->begin_reversible_command (_("change fade in length"));
2134
2135         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2136
2137                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2138
2139                 if (!tmp) {
2140                         continue;
2141                 }
2142
2143                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2144                 XMLNode &before = alist->get_state();
2145
2146                 tmp->audio_region()->set_fade_in_length (fade_length);
2147                 tmp->audio_region()->set_fade_in_active (true);
2148                 tmp->hide_fade_line();
2149
2150                 XMLNode &after = alist->get_state();
2151                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2152         }
2153
2154         _editor->commit_reversible_command ();
2155 }
2156
2157 void
2158 FadeInDrag::aborted ()
2159 {
2160         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2161                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2162
2163                 if (!tmp) {
2164                         continue;
2165                 }
2166
2167                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2168                 tmp->hide_fade_line();
2169         }
2170 }
2171
2172 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2173         : RegionDrag (e, i, p, v)
2174 {
2175         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2176 }
2177
2178 void
2179 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2180 {
2181         Drag::start_grab (event, cursor);
2182
2183         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2184         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2185
2186         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2187         _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2188         
2189         arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2190 }
2191
2192 void
2193 FadeOutDrag::motion (GdkEvent* event, bool)
2194 {
2195         framecnt_t fade_length;
2196
2197         framepos_t const pos = adjusted_current_frame (event);
2198
2199         boost::shared_ptr<Region> region = _primary->region ();
2200
2201         if (pos > (region->last_frame() - 64)) {
2202                 fade_length = 64; // this should really be a minimum fade defined somewhere
2203         }
2204         else if (pos < region->position()) {
2205                 fade_length = region->length();
2206         }
2207         else {
2208                 fade_length = region->last_frame() - pos;
2209         }
2210
2211         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2212
2213                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2214
2215                 if (!tmp) {
2216                         continue;
2217                 }
2218
2219                 tmp->reset_fade_out_shape_width (fade_length);
2220                 tmp->show_fade_line(region->length() - fade_length);
2221         }
2222
2223         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2224 }
2225
2226 void
2227 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2228 {
2229         if (!movement_occurred) {
2230                 return;
2231         }
2232
2233         framecnt_t fade_length;
2234
2235         framepos_t const pos = adjusted_current_frame (event);
2236
2237         boost::shared_ptr<Region> region = _primary->region ();
2238
2239         if (pos > (region->last_frame() - 64)) {
2240                 fade_length = 64; // this should really be a minimum fade defined somewhere
2241         }
2242         else if (pos < region->position()) {
2243                 fade_length = region->length();
2244         }
2245         else {
2246                 fade_length = region->last_frame() - pos;
2247         }
2248
2249         _editor->begin_reversible_command (_("change fade out length"));
2250
2251         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2252
2253                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2254
2255                 if (!tmp) {
2256                         continue;
2257                 }
2258
2259                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2260                 XMLNode &before = alist->get_state();
2261
2262                 tmp->audio_region()->set_fade_out_length (fade_length);
2263                 tmp->audio_region()->set_fade_out_active (true);
2264                 tmp->hide_fade_line();
2265
2266                 XMLNode &after = alist->get_state();
2267                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2268         }
2269
2270         _editor->commit_reversible_command ();
2271 }
2272
2273 void
2274 FadeOutDrag::aborted ()
2275 {
2276         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2277                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2278
2279                 if (!tmp) {
2280                         continue;
2281                 }
2282
2283                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2284                 tmp->hide_fade_line();
2285         }
2286 }
2287
2288 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2289         : Drag (e, i)
2290 {
2291         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2292         
2293         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2294         assert (_marker);
2295
2296         _points.push_back (Gnome::Art::Point (0, 0));
2297         _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2298
2299         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2300         _line->property_width_pixels() = 1;
2301         _line->property_points () = _points;
2302         _line->hide ();
2303
2304         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2305 }
2306
2307 MarkerDrag::~MarkerDrag ()
2308 {
2309         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2310                 delete *i;
2311         }
2312 }
2313
2314 void
2315 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2316 {
2317         Drag::start_grab (event, cursor);
2318
2319         bool is_start;
2320
2321         Location *location = _editor->find_location_from_marker (_marker, is_start);
2322         _editor->_dragging_edit_point = true;
2323
2324         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2325
2326         update_item (location);
2327
2328         // _drag_line->show();
2329         // _line->raise_to_top();
2330
2331         if (is_start) {
2332                 _editor->show_verbose_time_cursor (location->start(), 10);
2333         } else {
2334                 _editor->show_verbose_time_cursor (location->end(), 10);
2335         }
2336
2337         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2338
2339         switch (op) {
2340         case Selection::Toggle:
2341                 _editor->selection->toggle (_marker);
2342                 break;
2343         case Selection::Set:
2344                 if (!_editor->selection->selected (_marker)) {
2345                         _editor->selection->set (_marker);
2346                 }
2347                 break;
2348         case Selection::Extend:
2349         {
2350                 Locations::LocationList ll;
2351                 list<Marker*> to_add;
2352                 framepos_t s, e;
2353                 _editor->selection->markers.range (s, e);
2354                 s = min (_marker->position(), s);
2355                 e = max (_marker->position(), e);
2356                 s = min (s, e);
2357                 e = max (s, e);
2358                 if (e < max_framepos) {
2359                         ++e;
2360                 }
2361                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2362                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2363                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2364                         if (lm) {
2365                                 if (lm->start) {
2366                                         to_add.push_back (lm->start);
2367                                 }
2368                                 if (lm->end) {
2369                                         to_add.push_back (lm->end);
2370                                 }
2371                         }
2372                 }
2373                 if (!to_add.empty()) {
2374                         _editor->selection->add (to_add);
2375                 }
2376                 break;
2377         }
2378         case Selection::Add:
2379                 _editor->selection->add (_marker);
2380                 break;
2381         }
2382
2383         /* Set up copies for us to manipulate during the drag */
2384
2385         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2386                 Location* l = _editor->find_location_from_marker (*i, is_start);
2387                 _copied_locations.push_back (new Location (*l));
2388         }
2389 }
2390
2391 void
2392 MarkerDrag::motion (GdkEvent* event, bool)
2393 {
2394         framecnt_t f_delta = 0;
2395         bool is_start;
2396         bool move_both = false;
2397         Marker* marker;
2398         Location *real_location;
2399         Location *copy_location = 0;
2400
2401         framepos_t const newframe = adjusted_current_frame (event);
2402
2403         framepos_t next = newframe;
2404
2405         if (newframe == last_pointer_frame()) {
2406                 return;
2407         }
2408
2409         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2410                 move_both = true;
2411         }
2412
2413         MarkerSelection::iterator i;
2414         list<Location*>::iterator x;
2415
2416         /* find the marker we're dragging, and compute the delta */
2417
2418         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2419              x != _copied_locations.end() && i != _editor->selection->markers.end();
2420              ++i, ++x) {
2421
2422                 copy_location = *x;
2423                 marker = *i;
2424
2425                 if (marker == _marker) {
2426
2427                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2428                                 /* que pasa ?? */
2429                                 return;
2430                         }
2431
2432                         if (real_location->is_mark()) {
2433                                 f_delta = newframe - copy_location->start();
2434                         } else {
2435
2436
2437                                 switch (marker->type()) {
2438                                 case Marker::SessionStart:
2439                                 case Marker::RangeStart:
2440                                 case Marker::LoopStart:
2441                                 case Marker::PunchIn:
2442                                         f_delta = newframe - copy_location->start();
2443                                         break;
2444
2445                                 case Marker::SessionEnd:
2446                                 case Marker::RangeEnd:
2447                                 case Marker::LoopEnd:
2448                                 case Marker::PunchOut:
2449                                         f_delta = newframe - copy_location->end();
2450                                         break;
2451                                 default:
2452                                         /* what kind of marker is this ? */
2453                                         return;
2454                                 }
2455                         }
2456                         break;
2457                 }
2458         }
2459
2460         if (i == _editor->selection->markers.end()) {
2461                 /* hmm, impossible - we didn't find the dragged marker */
2462                 return;
2463         }
2464
2465         /* now move them all */
2466
2467         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2468              x != _copied_locations.end() && i != _editor->selection->markers.end();
2469              ++i, ++x) {
2470
2471                 copy_location = *x;
2472                 marker = *i;
2473
2474                 /* call this to find out if its the start or end */
2475
2476                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2477                         continue;
2478                 }
2479
2480                 if (real_location->locked()) {
2481                         continue;
2482                 }
2483
2484                 if (copy_location->is_mark()) {
2485
2486                         /* now move it */
2487
2488                         copy_location->set_start (copy_location->start() + f_delta);
2489
2490                 } else {
2491
2492                         framepos_t new_start = copy_location->start() + f_delta;
2493                         framepos_t new_end = copy_location->end() + f_delta;
2494
2495                         if (is_start) { // start-of-range marker
2496
2497                                 if (move_both) {
2498                                         copy_location->set_start (new_start);
2499                                         copy_location->set_end (new_end);
2500                                 } else  if (new_start < copy_location->end()) {
2501                                         copy_location->set_start (new_start);
2502                                 } else {
2503                                         _editor->snap_to (next, 1, true);
2504                                         copy_location->set_end (next);
2505                                         copy_location->set_start (newframe);
2506                                 }
2507
2508                         } else { // end marker
2509
2510                                 if (move_both) {
2511                                         copy_location->set_end (new_end);
2512                                         copy_location->set_start (new_start);
2513                                 } else if (new_end > copy_location->start()) {
2514                                         copy_location->set_end (new_end);
2515                                 } else if (newframe > 0) {
2516                                         _editor->snap_to (next, -1, true);
2517                                         copy_location->set_start (next);
2518                                         copy_location->set_end (newframe);
2519                                 }
2520                         }
2521                 }
2522
2523                 update_item (copy_location);
2524
2525                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2526
2527                 if (lm) {
2528                         lm->set_position (copy_location->start(), copy_location->end());
2529                 }
2530         }
2531
2532         assert (!_copied_locations.empty());
2533
2534         _editor->show_verbose_time_cursor (newframe, 10);
2535
2536 #ifdef GTKOSX
2537         _editor->update_canvas_now ();
2538 #endif
2539 }
2540
2541 void
2542 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2543 {
2544         if (!movement_occurred) {
2545
2546                 /* just a click, do nothing but finish
2547                    off the selection process
2548                 */
2549
2550                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2551
2552                 switch (op) {
2553                 case Selection::Set:
2554                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2555                                 _editor->selection->set (_marker);
2556                         }
2557                         break;
2558
2559                 case Selection::Toggle:
2560                 case Selection::Extend:
2561                 case Selection::Add:
2562                         break;
2563                 }
2564
2565                 return;
2566         }
2567
2568         _editor->_dragging_edit_point = false;
2569
2570         _editor->begin_reversible_command ( _("move marker") );
2571         XMLNode &before = _editor->session()->locations()->get_state();
2572
2573         MarkerSelection::iterator i;
2574         list<Location*>::iterator x;
2575         bool is_start;
2576
2577         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2578              x != _copied_locations.end() && i != _editor->selection->markers.end();
2579              ++i, ++x) {
2580
2581                 Location * location = _editor->find_location_from_marker (*i, is_start);
2582
2583                 if (location) {
2584
2585                         if (location->locked()) {
2586                                 return;
2587                         }
2588
2589                         if (location->is_mark()) {
2590                                 location->set_start ((*x)->start());
2591                         } else {
2592                                 location->set ((*x)->start(), (*x)->end());
2593                         }
2594                 }
2595         }
2596
2597         XMLNode &after = _editor->session()->locations()->get_state();
2598         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2599         _editor->commit_reversible_command ();
2600
2601         _line->hide();
2602 }
2603
2604 void
2605 MarkerDrag::aborted ()
2606 {
2607         /* XXX: TODO */
2608 }
2609
2610 void
2611 MarkerDrag::update_item (Location* location)
2612 {
2613         double const x1 = _editor->frame_to_pixel (location->start());
2614
2615         _points.front().set_x(x1);
2616         _points.back().set_x(x1);
2617         _line->property_points() = _points;
2618 }
2619
2620 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2621         : Drag (e, i),
2622           _cumulative_x_drag (0),
2623           _cumulative_y_drag (0)
2624 {
2625         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2626         
2627         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2628         assert (_point);
2629 }
2630
2631
2632 void
2633 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2634 {
2635         Drag::start_grab (event, _editor->cursors()->fader);
2636
2637         // start the grab at the center of the control point so
2638         // the point doesn't 'jump' to the mouse after the first drag
2639         _fixed_grab_x = _point->get_x();
2640         _fixed_grab_y = _point->get_y();
2641
2642         float const fraction = 1 - (_point->get_y() / _point->line().height());
2643
2644         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2645
2646         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2647                                             event->button.x + 10, event->button.y + 10);
2648
2649         _editor->show_verbose_canvas_cursor ();
2650 }
2651
2652 void
2653 ControlPointDrag::motion (GdkEvent* event, bool)
2654 {
2655         double dx = _drags->current_pointer_x() - last_pointer_x();
2656         double dy = _drags->current_pointer_y() - last_pointer_y();
2657
2658         if (event->button.state & Keyboard::SecondaryModifier) {
2659                 dx *= 0.1;
2660                 dy *= 0.1;
2661         }
2662
2663         /* coordinate in pixels relative to the start of the region (for region-based automation)
2664            or track (for track-based automation) */
2665         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2666         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2667
2668         // calculate zero crossing point. back off by .01 to stay on the
2669         // positive side of zero
2670         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2671
2672         // make sure we hit zero when passing through
2673         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2674                 cy = zero_gain_y;
2675         }
2676
2677         if (_x_constrained) {
2678                 cx = _fixed_grab_x;
2679         }
2680         if (_y_constrained) {
2681                 cy = _fixed_grab_y;
2682         }
2683
2684         _cumulative_x_drag = cx - _fixed_grab_x;
2685         _cumulative_y_drag = cy - _fixed_grab_y;
2686
2687         cx = max (0.0, cx);
2688         cy = max (0.0, cy);
2689         cy = min ((double) _point->line().height(), cy);
2690
2691         framepos_t cx_frames = _editor->unit_to_frame (cx);
2692         
2693         if (!_x_constrained) {
2694                 _editor->snap_to_with_modifier (cx_frames, event);
2695         }
2696
2697         cx_frames = min (cx_frames, _point->line().maximum_time());
2698
2699         float const fraction = 1.0 - (cy / _point->line().height());
2700
2701         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2702
2703         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2704
2705         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2706 }
2707
2708 void
2709 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2710 {
2711         if (!movement_occurred) {
2712
2713                 /* just a click */
2714
2715                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2716                         _editor->reset_point_selection ();
2717                 }
2718
2719         } else {
2720                 motion (event, false);
2721         }
2722         
2723         _point->line().end_drag ();
2724         _editor->session()->commit_reversible_command ();
2725 }
2726
2727 void
2728 ControlPointDrag::aborted ()
2729 {
2730         _point->line().reset ();
2731 }
2732
2733 bool
2734 ControlPointDrag::active (Editing::MouseMode m)
2735 {
2736         if (m == Editing::MouseGain) {
2737                 /* always active in mouse gain */
2738                 return true;
2739         }
2740
2741         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2742         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2743 }
2744
2745 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2746         : Drag (e, i),
2747           _line (0),
2748           _cumulative_y_drag (0)
2749 {
2750         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2751 }
2752
2753 void
2754 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2755 {
2756         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2757         assert (_line);
2758
2759         _item = &_line->grab_item ();
2760
2761         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2762            origin, and ditto for y.
2763         */
2764
2765         double cx = event->button.x;
2766         double cy = event->button.y;
2767
2768         _line->parent_group().w2i (cx, cy);
2769
2770         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2771
2772         uint32_t before;
2773         uint32_t after;
2774         
2775         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2776                 /* no adjacent points */
2777                 return;
2778         }
2779
2780         Drag::start_grab (event, _editor->cursors()->fader);
2781
2782         /* store grab start in parent frame */
2783
2784         _fixed_grab_x = cx;
2785         _fixed_grab_y = cy;
2786
2787         double fraction = 1.0 - (cy / _line->height());
2788
2789         _line->start_drag_line (before, after, fraction);
2790
2791         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2792                                             event->button.x + 10, event->button.y + 10);
2793
2794         _editor->show_verbose_canvas_cursor ();
2795 }
2796
2797 void
2798 LineDrag::motion (GdkEvent* event, bool)
2799 {
2800         double dy = _drags->current_pointer_y() - last_pointer_y();
2801
2802         if (event->button.state & Keyboard::SecondaryModifier) {
2803                 dy *= 0.1;
2804         }
2805
2806         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2807
2808         _cumulative_y_drag = cy - _fixed_grab_y;
2809
2810         cy = max (0.0, cy);
2811         cy = min ((double) _line->height(), cy);
2812
2813         double const fraction = 1.0 - (cy / _line->height());
2814
2815         bool push;
2816
2817         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2818                 push = false;
2819         } else {
2820                 push = true;
2821         }
2822
2823         /* we are ignoring x position for this drag, so we can just pass in anything */
2824         _line->drag_motion (0, fraction, true, push);
2825
2826         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2827 }
2828
2829 void
2830 LineDrag::finished (GdkEvent* event, bool)
2831 {
2832         motion (event, false);
2833         _line->end_drag ();
2834         _editor->session()->commit_reversible_command ();
2835 }
2836
2837 void
2838 LineDrag::aborted ()
2839 {
2840         _line->reset ();
2841 }
2842
2843 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2844         : Drag (e, i),
2845           _line (0),
2846           _cumulative_x_drag (0)
2847 {
2848         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2849 }
2850
2851 void
2852 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2853 {
2854         Drag::start_grab (event);
2855         
2856         _line = reinterpret_cast<SimpleLine*> (_item);
2857         assert (_line);
2858
2859         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2860
2861         double cx = event->button.x;
2862         double cy = event->button.y;
2863
2864         _item->property_parent().get_value()->w2i(cx, cy);
2865
2866         /* store grab start in parent frame */
2867         _region_view_grab_x = cx;
2868         
2869         _before = _line->property_x1();
2870         
2871         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2872                 
2873         _max_x = _editor->frame_to_pixel(_arv->get_duration());
2874 }
2875
2876 void
2877 FeatureLineDrag::motion (GdkEvent*, bool)
2878 {
2879         double dx = _drags->current_pointer_x() - last_pointer_x();
2880         
2881         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2882         
2883         _cumulative_x_drag += dx;
2884                 
2885         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2886         
2887         if (cx > _max_x){
2888                 cx = _max_x;
2889         }
2890         else if(cx < 0){
2891                 cx = 0;
2892         }
2893         
2894         _line->property_x1() = cx; 
2895         _line->property_x2() = cx;
2896
2897         _before = _line->property_x1();
2898 }
2899
2900 void
2901 FeatureLineDrag::finished (GdkEvent*, bool)
2902 {
2903         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2904         _arv->update_transient(_before, _line->property_x1());
2905 }
2906
2907 void
2908 FeatureLineDrag::aborted ()
2909 {
2910         //_line->reset ();
2911 }
2912
2913 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2914         : Drag (e, i)
2915 {
2916         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2917 }
2918
2919 void
2920 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2921 {
2922         Drag::start_grab (event);
2923         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2924 }
2925
2926 void
2927 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2928 {
2929         framepos_t start;
2930         framepos_t end;
2931         double y1;
2932         double y2;
2933
2934         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2935
2936         framepos_t grab = grab_frame ();
2937         if (Config->get_rubberbanding_snaps_to_grid ()) {
2938                 _editor->snap_to_with_modifier (grab, event);
2939         }
2940
2941         /* base start and end on initial click position */
2942
2943         if (pf < grab) {
2944                 start = pf;
2945                 end = grab;
2946         } else {
2947                 end = pf;
2948                 start = grab;
2949         }
2950
2951         if (_drags->current_pointer_y() < grab_y()) {
2952                 y1 = _drags->current_pointer_y();
2953                 y2 = grab_y();
2954         } else {
2955                 y2 = _drags->current_pointer_y();
2956                 y1 = grab_y();
2957         }
2958
2959
2960         if (start != end || y1 != y2) {
2961
2962                 double x1 = _editor->frame_to_pixel (start);
2963                 double x2 = _editor->frame_to_pixel (end);
2964
2965                 _editor->rubberband_rect->property_x1() = x1;
2966                 _editor->rubberband_rect->property_y1() = y1;
2967                 _editor->rubberband_rect->property_x2() = x2;
2968                 _editor->rubberband_rect->property_y2() = y2;
2969
2970                 _editor->rubberband_rect->show();
2971                 _editor->rubberband_rect->raise_to_top();
2972
2973                 _editor->show_verbose_time_cursor (pf, 10);
2974         }
2975 }
2976
2977 void
2978 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2979 {
2980         if (movement_occurred) {
2981
2982                 motion (event, false);
2983
2984                 double y1,y2;
2985                 if (_drags->current_pointer_y() < grab_y()) {
2986                         y1 = _drags->current_pointer_y();
2987                         y2 = grab_y();
2988                 } else {
2989                         y2 = _drags->current_pointer_y();
2990                         y1 = grab_y();
2991                 }
2992
2993
2994                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2995                 bool committed;
2996
2997                 _editor->begin_reversible_command (_("rubberband selection"));
2998
2999                 if (grab_frame() < last_pointer_frame()) {
3000                         committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3001                 } else {
3002                         committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3003                 }
3004
3005                 if (!committed) {
3006                         _editor->commit_reversible_command ();
3007                 }
3008
3009         } else {
3010                 if (!getenv("ARDOUR_SAE")) {
3011                         _editor->selection->clear_tracks();
3012                 }
3013                 _editor->selection->clear_regions();
3014                 _editor->selection->clear_points ();
3015                 _editor->selection->clear_lines ();
3016         }
3017
3018         _editor->rubberband_rect->hide();
3019 }
3020
3021 void
3022 RubberbandSelectDrag::aborted ()
3023 {
3024         _editor->rubberband_rect->hide ();
3025 }
3026
3027 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3028         : RegionDrag (e, i, p, v)
3029 {
3030         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3031 }
3032
3033 void
3034 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3035 {
3036         Drag::start_grab (event, cursor);
3037
3038         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3039 }
3040
3041 void
3042 TimeFXDrag::motion (GdkEvent* event, bool)
3043 {
3044         RegionView* rv = _primary;
3045
3046         framepos_t const pf = adjusted_current_frame (event);
3047
3048         if (pf > rv->region()->position()) {
3049                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3050         }
3051
3052         _editor->show_verbose_time_cursor (pf, 10);
3053 }
3054
3055 void
3056 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3057 {
3058         _primary->get_time_axis_view().hide_timestretch ();
3059
3060         if (!movement_occurred) {
3061                 return;
3062         }
3063
3064         if (last_pointer_frame() < _primary->region()->position()) {
3065                 /* backwards drag of the left edge - not usable */
3066                 return;
3067         }
3068
3069         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3070
3071         float percentage = (double) newlen / (double) _primary->region()->length();
3072
3073 #ifndef USE_RUBBERBAND
3074         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3075         if (_primary->region()->data_type() == DataType::AUDIO) {
3076                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3077         }
3078 #endif
3079
3080         _editor->begin_reversible_command (_("timestretch"));
3081
3082         // XXX how do timeFX on multiple regions ?
3083
3084         RegionSelection rs;
3085         rs.add (_primary);
3086
3087         if (_editor->time_stretch (rs, percentage) == -1) {
3088                 error << _("An error occurred while executing time stretch operation") << endmsg;
3089         }
3090 }
3091
3092 void
3093 TimeFXDrag::aborted ()
3094 {
3095         _primary->get_time_axis_view().hide_timestretch ();
3096 }
3097
3098 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3099         : Drag (e, i)
3100 {
3101         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3102 }
3103
3104 void
3105 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3106 {
3107         Drag::start_grab (event);
3108 }
3109
3110 void
3111 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3112 {
3113         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3114 }
3115
3116 void
3117 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3118 {
3119         if (movement_occurred && _editor->session()) {
3120                 /* make sure we stop */
3121                 _editor->session()->request_transport_speed (0.0);
3122         }
3123 }
3124
3125 void
3126 ScrubDrag::aborted ()
3127 {
3128         /* XXX: TODO */
3129 }
3130
3131 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3132         : Drag (e, i)
3133         , _operation (o)
3134         , _copy (false)
3135         , _original_pointer_time_axis (-1)
3136         , _last_pointer_time_axis (-1)
3137 {
3138         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3139 }
3140
3141 void
3142 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3143 {
3144         framepos_t start = 0;
3145         framepos_t end = 0;
3146
3147         if (_editor->session() == 0) {
3148                 return;
3149         }
3150
3151         Gdk::Cursor* cursor = 0;
3152
3153         switch (_operation) {
3154         case CreateSelection:
3155                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3156                         _copy = true;
3157                 } else {
3158                         _copy = false;
3159                 }
3160                 cursor = _editor->cursors()->selector;
3161                 Drag::start_grab (event, cursor);
3162                 break;
3163
3164         case SelectionStartTrim:
3165                 if (_editor->clicked_axisview) {
3166                         _editor->clicked_axisview->order_selection_trims (_item, true);
3167                 }
3168                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3169                 start = _editor->selection->time[_editor->clicked_selection].start;
3170                 _pointer_frame_offset = raw_grab_frame() - start;
3171                 break;
3172
3173         case SelectionEndTrim:
3174                 if (_editor->clicked_axisview) {
3175                         _editor->clicked_axisview->order_selection_trims (_item, false);
3176                 }
3177                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3178                 end = _editor->selection->time[_editor->clicked_selection].end;
3179                 _pointer_frame_offset = raw_grab_frame() - end;
3180                 break;
3181
3182         case SelectionMove:
3183                 start = _editor->selection->time[_editor->clicked_selection].start;
3184                 Drag::start_grab (event, cursor);
3185                 _pointer_frame_offset = raw_grab_frame() - start;
3186                 break;
3187         }
3188
3189         if (_operation == SelectionMove) {
3190                 _editor->show_verbose_time_cursor (start, 10);
3191         } else {
3192                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3193         }
3194
3195         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3196 }
3197
3198 void
3199 SelectionDrag::motion (GdkEvent* event, bool first_move)
3200 {
3201         framepos_t start = 0;
3202         framepos_t end = 0;
3203         framecnt_t length;
3204
3205         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3206         if (pending_time_axis.first == 0) {
3207                 return;
3208         }
3209         
3210         framepos_t const pending_position = adjusted_current_frame (event);
3211
3212         /* only alter selection if things have changed */
3213
3214         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3215                 return;
3216         }
3217
3218         switch (_operation) {
3219         case CreateSelection:
3220         {
3221                 framepos_t grab = grab_frame ();
3222
3223                 if (first_move) {
3224                         _editor->snap_to (grab);
3225                 }
3226
3227                 if (pending_position < grab_frame()) {
3228                         start = pending_position;
3229                         end = grab;
3230                 } else {
3231                         end = pending_position;
3232                         start = grab;
3233                 }
3234
3235                 /* first drag: Either add to the selection
3236                    or create a new selection
3237                 */
3238
3239                 if (first_move) {
3240
3241                         if (_copy) {
3242                                 /* adding to the selection */
3243                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3244                                 //_editor->selection->add (_editor->clicked_axisview);
3245                                 _editor->clicked_selection = _editor->selection->add (start, end);
3246                                 _copy = false;
3247                         } else {
3248                                 /* new selection */
3249
3250                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3251                                         //_editor->selection->set (_editor->clicked_axisview);
3252                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3253                                 }
3254                                 
3255                                 _editor->clicked_selection = _editor->selection->set (start, end);
3256                         }
3257                 }
3258
3259                 /* select the track that we're in */
3260                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3261                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3262                         _editor->selection->add (pending_time_axis.first);
3263                         _added_time_axes.push_back (pending_time_axis.first);
3264                 }
3265
3266                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3267                    tracks that we selected in the first place.
3268                 */
3269                 
3270                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3271                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3272
3273                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3274                 while (i != _added_time_axes.end()) {
3275
3276                         list<TimeAxisView*>::iterator tmp = i;
3277                         ++tmp;
3278                         
3279                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3280                                 _editor->selection->remove (*i);
3281                                 _added_time_axes.remove (*i);
3282                         }
3283
3284                         i = tmp;
3285                 }
3286
3287         }
3288         break;
3289
3290         case SelectionStartTrim:
3291
3292                 start = _editor->selection->time[_editor->clicked_selection].start;
3293                 end = _editor->selection->time[_editor->clicked_selection].end;
3294
3295                 if (pending_position > end) {
3296                         start = end;
3297                 } else {
3298                         start = pending_position;
3299                 }
3300                 break;
3301
3302         case SelectionEndTrim:
3303
3304                 start = _editor->selection->time[_editor->clicked_selection].start;
3305                 end = _editor->selection->time[_editor->clicked_selection].end;
3306
3307                 if (pending_position < start) {
3308                         end = start;
3309                 } else {
3310                         end = pending_position;
3311                 }
3312
3313                 break;
3314
3315         case SelectionMove:
3316
3317                 start = _editor->selection->time[_editor->clicked_selection].start;
3318                 end = _editor->selection->time[_editor->clicked_selection].end;
3319
3320                 length = end - start;
3321
3322                 start = pending_position;
3323                 _editor->snap_to (start);
3324
3325                 end = start + length;
3326
3327                 break;
3328         }
3329
3330         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3331                 _editor->start_canvas_autoscroll (1, 0);
3332         }
3333
3334         if (start != end) {
3335                 _editor->selection->replace (_editor->clicked_selection, start, end);
3336         }
3337
3338         if (_operation == SelectionMove) {
3339                 _editor->show_verbose_time_cursor(start, 10);
3340         } else {
3341                 _editor->show_verbose_time_cursor(pending_position, 10);
3342         }
3343 }
3344
3345 void
3346 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3347 {
3348         Session* s = _editor->session();
3349
3350         if (movement_occurred) {
3351                 motion (event, false);
3352                 /* XXX this is not object-oriented programming at all. ick */
3353                 if (_editor->selection->time.consolidate()) {
3354                         _editor->selection->TimeChanged ();
3355                 }
3356
3357                 /* XXX what if its a music time selection? */
3358                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3359                         s->request_play_range (&_editor->selection->time, true);
3360                 }
3361
3362
3363         } else {
3364                 /* just a click, no pointer movement.*/
3365
3366                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3367                         _editor->selection->clear_time();
3368                 }
3369
3370                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3371                         _editor->selection->set (_editor->clicked_axisview);
3372                 }
3373                 
3374                 if (s && s->get_play_range () && s->transport_rolling()) {
3375                         s->request_stop (false, false);
3376                 }
3377
3378         }
3379
3380         _editor->stop_canvas_autoscroll ();
3381 }
3382
3383 void
3384 SelectionDrag::aborted ()
3385 {
3386         /* XXX: TODO */
3387 }
3388
3389 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3390         : Drag (e, i),
3391           _operation (o),
3392           _copy (false)
3393 {
3394         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3395         
3396         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, 
3397                                                    physical_screen_height (_editor->get_window()));
3398         _drag_rect->hide ();
3399
3400         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3401         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3402 }
3403
3404 void
3405 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3406 {
3407         if (_editor->session() == 0) {
3408                 return;
3409         }
3410
3411         Gdk::Cursor* cursor = 0;
3412
3413         if (!_editor->temp_location) {
3414                 _editor->temp_location = new Location (*_editor->session());
3415         }
3416
3417         switch (_operation) {
3418         case CreateRangeMarker:
3419         case CreateTransportMarker:
3420         case CreateCDMarker:
3421
3422                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3423                         _copy = true;
3424                 } else {
3425                         _copy = false;
3426                 }
3427                 cursor = _editor->cursors()->selector;
3428                 break;
3429         }
3430
3431         Drag::start_grab (event, cursor);
3432
3433         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3434 }
3435
3436 void
3437 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3438 {
3439         framepos_t start = 0;
3440         framepos_t end = 0;
3441         ArdourCanvas::SimpleRect *crect;
3442
3443         switch (_operation) {
3444         case CreateRangeMarker:
3445                 crect = _editor->range_bar_drag_rect;
3446                 break;
3447         case CreateTransportMarker:
3448                 crect = _editor->transport_bar_drag_rect;
3449                 break;
3450         case CreateCDMarker:
3451                 crect = _editor->cd_marker_bar_drag_rect;
3452                 break;
3453         default:
3454                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3455                 return;
3456                 break;
3457         }
3458
3459         framepos_t const pf = adjusted_current_frame (event);
3460
3461         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3462                 framepos_t grab = grab_frame ();
3463                 _editor->snap_to (grab);
3464                 
3465                 if (pf < grab_frame()) {
3466                         start = pf;
3467                         end = grab;
3468                 } else {
3469                         end = pf;
3470                         start = grab;
3471                 }
3472
3473                 /* first drag: Either add to the selection
3474                    or create a new selection.
3475                 */
3476
3477                 if (first_move) {
3478
3479                         _editor->temp_location->set (start, end);
3480
3481                         crect->show ();
3482
3483                         update_item (_editor->temp_location);
3484                         _drag_rect->show();
3485                         //_drag_rect->raise_to_top();
3486
3487                 }
3488         }
3489
3490         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3491                 _editor->start_canvas_autoscroll (1, 0);
3492         }
3493
3494         if (start != end) {
3495                 _editor->temp_location->set (start, end);
3496
3497                 double x1 = _editor->frame_to_pixel (start);
3498                 double x2 = _editor->frame_to_pixel (end);
3499                 crect->property_x1() = x1;
3500                 crect->property_x2() = x2;
3501
3502                 update_item (_editor->temp_location);
3503         }
3504
3505         _editor->show_verbose_time_cursor (pf, 10);
3506
3507 }
3508
3509 void
3510 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3511 {
3512         Location * newloc = 0;
3513         string rangename;
3514         int flags;
3515
3516         if (movement_occurred) {
3517                 motion (event, false);
3518                 _drag_rect->hide();
3519
3520                 switch (_operation) {
3521                 case CreateRangeMarker:
3522                 case CreateCDMarker:
3523                     {
3524                         _editor->begin_reversible_command (_("new range marker"));
3525                         XMLNode &before = _editor->session()->locations()->get_state();
3526                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3527                         if (_operation == CreateCDMarker) {
3528                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3529                                 _editor->cd_marker_bar_drag_rect->hide();
3530                         }
3531                         else {
3532                                 flags = Location::IsRangeMarker;
3533                                 _editor->range_bar_drag_rect->hide();
3534                         }
3535                         newloc = new Location (
3536                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3537                                 );
3538                         
3539                         _editor->session()->locations()->add (newloc, true);
3540                         XMLNode &after = _editor->session()->locations()->get_state();
3541                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3542                         _editor->commit_reversible_command ();
3543                         break;
3544                     }
3545
3546                 case CreateTransportMarker:
3547                         // popup menu to pick loop or punch
3548                         _editor->new_transport_marker_context_menu (&event->button, _item);
3549                         break;
3550                 }
3551         } else {
3552                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3553
3554                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3555
3556                         framepos_t start;
3557                         framepos_t end;
3558
3559                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3560
3561                         if (end == max_framepos) {
3562                                 end = _editor->session()->current_end_frame ();
3563                         }
3564
3565                         if (start == max_framepos) {
3566                                 start = _editor->session()->current_start_frame ();
3567                         }
3568
3569                         switch (_editor->mouse_mode) {
3570                         case MouseObject:
3571                                 /* find the two markers on either side and then make the selection from it */
3572                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3573                                 break;
3574
3575                         case MouseRange:
3576                                 /* find the two markers on either side of the click and make the range out of it */
3577                                 _editor->selection->set (start, end);
3578                                 break;
3579
3580                         default:
3581                                 break;
3582                         }
3583                 }
3584         }
3585
3586         _editor->stop_canvas_autoscroll ();
3587 }
3588
3589 void
3590 RangeMarkerBarDrag::aborted ()
3591 {
3592         /* XXX: TODO */
3593 }
3594
3595 void
3596 RangeMarkerBarDrag::update_item (Location* location)
3597 {
3598         double const x1 = _editor->frame_to_pixel (location->start());
3599         double const x2 = _editor->frame_to_pixel (location->end());
3600
3601         _drag_rect->property_x1() = x1;
3602         _drag_rect->property_x2() = x2;
3603 }
3604
3605 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3606         : Drag (e, i)
3607         , _zoom_out (false)
3608 {
3609         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3610 }
3611
3612 void
3613 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3614 {
3615         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3616                 Drag::start_grab (event, _editor->cursors()->zoom_out);
3617                 _zoom_out = true;
3618         } else {
3619                 Drag::start_grab (event, _editor->cursors()->zoom_in);
3620                 _zoom_out = false;
3621         }
3622                 
3623         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3624 }
3625
3626 void
3627 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3628 {
3629         framepos_t start;
3630         framepos_t end;
3631
3632         framepos_t const pf = adjusted_current_frame (event);
3633
3634         framepos_t grab = grab_frame ();
3635         _editor->snap_to_with_modifier (grab, event);
3636
3637         /* base start and end on initial click position */
3638         if (pf < grab) {
3639                 start = pf;
3640                 end = grab;
3641         } else {
3642                 end = pf;
3643                 start = grab;
3644         }
3645
3646         if (start != end) {
3647
3648                 if (first_move) {
3649                         _editor->zoom_rect->show();
3650                         _editor->zoom_rect->raise_to_top();
3651                 }
3652
3653                 _editor->reposition_zoom_rect(start, end);
3654
3655                 _editor->show_verbose_time_cursor (pf, 10);
3656         }
3657 }
3658
3659 void
3660 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3661 {
3662         if (movement_occurred) {
3663                 motion (event, false);
3664
3665                 if (grab_frame() < last_pointer_frame()) {
3666                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3667                 } else {
3668                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3669                 }
3670         } else {
3671                 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3672         }
3673
3674         _editor->zoom_rect->hide();
3675 }
3676
3677 void
3678 MouseZoomDrag::aborted ()
3679 {
3680         _editor->zoom_rect->hide ();
3681 }
3682
3683 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3684         : Drag (e, i)
3685         , _cumulative_dx (0)
3686         , _cumulative_dy (0)
3687 {
3688         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3689
3690         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3691         _region = &_primary->region_view ();
3692         _note_height = _region->midi_stream_view()->note_height ();
3693 }
3694
3695 void
3696 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3697 {
3698         Drag::start_grab (event);
3699
3700         if (!(_was_selected = _primary->selected())) {
3701
3702                 /* tertiary-click means extend selection - we'll do that on button release,
3703                    so don't add it here, because otherwise we make it hard to figure
3704                    out the "extend-to" range.
3705                 */
3706
3707                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3708
3709                 if (!extend) {
3710                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3711
3712                         if (add) {
3713                                 _region->note_selected (_primary, true);
3714                         } else {
3715                                 _region->unique_select (_primary);
3716                         }
3717                 }
3718         }
3719 }
3720
3721 /** @return Current total drag x change in frames */
3722 frameoffset_t
3723 NoteDrag::total_dx () const
3724 {
3725         /* dx in frames */
3726         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3727
3728         /* primary note time */
3729         frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3730         
3731         /* new time of the primary note relative to the region position */
3732         frameoffset_t const st = n + dx;
3733
3734         /* snap and return corresponding delta */
3735         return _region->snap_frame_to_frame (st) - n;
3736 }
3737
3738 /** @return Current total drag y change in notes */
3739 int8_t
3740 NoteDrag::total_dy () const
3741 {
3742         /* this is `backwards' to make increasing note number go in the right direction */
3743         double const dy = _drags->current_pointer_y() - grab_y();
3744
3745         /* dy in notes */
3746         int8_t ndy = 0;
3747
3748         if (abs (dy) >= _note_height) {
3749                 if (dy > 0) {
3750                         ndy = (int8_t) ceil (dy / _note_height / 2.0);
3751                 } else {
3752                         ndy = (int8_t) floor (dy / _note_height / 2.0);
3753                 }
3754         }
3755
3756         /* more positive value = higher pitch and higher y-axis position on track,
3757            which is the inverse of the X-centric geometric universe 
3758         */
3759
3760         return -ndy; 
3761 }       
3762
3763 void
3764 NoteDrag::motion (GdkEvent *, bool)
3765 {
3766         /* Total change in x and y since the start of the drag */
3767         frameoffset_t const dx = total_dx ();
3768         int8_t const dy = -total_dy ();
3769
3770         /* Now work out what we have to do to the note canvas items to set this new drag delta */
3771         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3772         double const tdy = dy * _note_height - _cumulative_dy;
3773
3774         if (tdx || tdy) {
3775                 _cumulative_dx += tdx;
3776                 _cumulative_dy += tdy;
3777
3778                 int8_t note_delta = total_dy();
3779
3780                 _region->move_selection (tdx, tdy, note_delta);
3781
3782                 char buf[12];
3783                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3784                           (int) floor (_primary->note()->note() + note_delta));
3785                 
3786                 _editor->show_verbose_canvas_cursor_with (buf);
3787         }
3788 }
3789
3790 void
3791 NoteDrag::finished (GdkEvent* ev, bool moved)
3792 {
3793         if (!moved) {
3794                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3795
3796                         if (_was_selected) {
3797                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3798                                 if (add) {
3799                                         _region->note_deselected (_primary);
3800                                 }
3801                         } else {
3802                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3803                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3804
3805                                 if (!extend && !add && _region->selection_size() > 1) {
3806                                         _region->unique_select (_primary);
3807                                 } else if (extend) {
3808                                         _region->note_selected (_primary, true, true);
3809                                 } else {
3810                                         /* it was added during button press */
3811                                 }
3812                         }
3813                 }
3814         } else {
3815                 _region->note_dropped (_primary, total_dx(), total_dy());
3816         }
3817 }
3818
3819 void
3820 NoteDrag::aborted ()
3821 {
3822         /* XXX: TODO */
3823 }
3824
3825 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3826         : Drag (editor, item)
3827         , _ranges (r)
3828         , _nothing_to_drag (false)
3829 {
3830         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3831         
3832         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3833         assert (_atav);
3834
3835         /* get all lines in the automation view */
3836         list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3837
3838         /* find those that overlap the ranges being dragged */
3839         list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3840         while (i != lines.end ()) {
3841                 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3842                 ++j;
3843
3844                 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3845
3846                 /* check this range against all the AudioRanges that we are using */
3847                 list<AudioRange>::const_iterator k = _ranges.begin ();
3848                 while (k != _ranges.end()) {
3849                         if (k->coverage (r.first, r.second) != OverlapNone) {
3850                                 break;
3851                         }
3852                         ++k;
3853                 }
3854
3855                 /* add it to our list if it overlaps at all */
3856                 if (k != _ranges.end()) {
3857                         Line n;
3858                         n.line = *i;
3859                         n.state = 0;
3860                         n.range = r;
3861                         _lines.push_back (n);
3862                 }
3863
3864                 i = j;
3865         }
3866
3867         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3868 }
3869
3870 void
3871 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3872 {
3873         Drag::start_grab (event, cursor);
3874
3875         /* Get line states before we start changing things */
3876         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3877                 i->state = &i->line->get_state ();
3878         }
3879
3880         if (_ranges.empty()) {
3881
3882                 /* No selected time ranges: drag all points */
3883                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3884                         uint32_t const N = i->line->npoints ();
3885                         for (uint32_t j = 0; j < N; ++j) {
3886                                 i->points.push_back (i->line->nth (j));
3887                         }
3888                 }
3889                 
3890         } else {
3891
3892                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3893
3894                         framecnt_t const half = (i->start + i->end) / 2;
3895                         
3896                         /* find the line that this audio range starts in */
3897                         list<Line>::iterator j = _lines.begin();
3898                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3899                                 ++j;
3900                         }
3901
3902                         if (j != _lines.end()) {
3903                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3904                                 
3905                                 /* j is the line that this audio range starts in; fade into it;
3906                                    64 samples length plucked out of thin air.
3907                                 */
3908
3909                                 framepos_t a = i->start + 64;
3910                                 if (a > half) {
3911                                         a = half;
3912                                 }
3913
3914                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3915                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3916
3917                                 the_list->add (p, the_list->eval (p));
3918                                 j->line->add_always_in_view (p);
3919                                 the_list->add (q, the_list->eval (q));
3920                                 j->line->add_always_in_view (q);
3921                         }
3922
3923                         /* same thing for the end */
3924                         
3925                         j = _lines.begin();
3926                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3927                                 ++j;
3928                         }
3929
3930                         if (j != _lines.end()) {
3931                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3932                                 
3933                                 /* j is the line that this audio range starts in; fade out of it;
3934                                    64 samples length plucked out of thin air.
3935                                 */
3936                                 
3937                                 framepos_t b = i->end - 64;
3938                                 if (b < half) {
3939                                         b = half;
3940                                 }
3941
3942                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3943                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3944                                 
3945                                 the_list->add (p, the_list->eval (p));
3946                                 j->line->add_always_in_view (p);
3947                                 the_list->add (q, the_list->eval (q));
3948                                 j->line->add_always_in_view (q);
3949                         }
3950                 }
3951
3952                 _nothing_to_drag = true;
3953
3954                 /* Find all the points that should be dragged and put them in the relevant
3955                    points lists in the Line structs.
3956                 */
3957
3958                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3959
3960                         uint32_t const N = i->line->npoints ();
3961                         for (uint32_t j = 0; j < N; ++j) {
3962
3963                                 /* here's a control point on this line */
3964                                 ControlPoint* p = i->line->nth (j);
3965                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
3966
3967                                 /* see if it's inside a range */
3968                                 list<AudioRange>::const_iterator k = _ranges.begin ();
3969                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
3970                                         ++k;
3971                                 }
3972
3973                                 if (k != _ranges.end()) {
3974                                         /* dragging this point */
3975                                         _nothing_to_drag = false;
3976                                         i->points.push_back (p);
3977                                 }
3978                         }
3979                 }
3980         }
3981
3982         if (_nothing_to_drag) {
3983                 return;
3984         }
3985
3986         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3987                 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
3988         }
3989 }
3990
3991 void
3992 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
3993 {
3994         if (_nothing_to_drag) {
3995                 return;
3996         }
3997
3998         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3999                 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4000
4001                 /* we are ignoring x position for this drag, so we can just pass in anything */
4002                 i->line->drag_motion (0, f, true, false);
4003         }
4004 }
4005
4006 void
4007 AutomationRangeDrag::finished (GdkEvent* event, bool)
4008 {
4009         if (_nothing_to_drag) {
4010                 return;
4011         }
4012         
4013         motion (event, false);
4014         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4015                 i->line->end_drag ();
4016                 i->line->clear_always_in_view ();
4017         }
4018
4019         _editor->session()->commit_reversible_command ();
4020 }
4021
4022 void
4023 AutomationRangeDrag::aborted ()
4024 {
4025         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4026                 i->line->clear_always_in_view ();
4027                 i->line->reset ();
4028         }
4029 }
4030
4031 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4032         : view (v)
4033 {
4034         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4035         layer = v->region()->layer ();
4036         initial_y = v->get_canvas_group()->property_y ();
4037         initial_playlist = v->region()->playlist ();
4038         initial_position = v->region()->position ();
4039         initial_end = v->region()->position () + v->region()->length ();
4040 }