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