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