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