8f59b9e699a40592986ee9a501b3fe99183f3e51
[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                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1008
1009                         frozen_insert_result = frozen_playlists.insert(playlist);
1010
1011                         if (frozen_insert_result.second) {
1012                                 playlist->freeze();
1013                         }
1014
1015                         XMLNode& before (rv->region()->get_state());
1016                         rv->region()->set_position (where, (void*) this);
1017                         _editor->session()->add_command (new MementoCommand<Region>(*rv->region(), &before, &playlist->get_state()));
1018
1019                 }
1020
1021                 if (changed_tracks && !_copy) {
1022
1023                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
1024                            because we may have copied the region and it has not been attached to a playlist.
1025                         */
1026
1027                         source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
1028                         ds = source_tv->get_diskstream();
1029                         from_playlist = ds->playlist();
1030
1031                         assert (source_tv);
1032                         assert (ds);
1033                         assert (from_playlist);
1034
1035                         /* moved to a different audio track, without copying */
1036
1037                         /* the region that used to be in the old playlist is not
1038                            moved to the new one - we use a copy of it. as a result,
1039                            any existing editor for the region should no longer be
1040                            visible.
1041                         */
1042
1043                         rv->hide_region_editor();
1044                         rv->fake_set_opaque (false);
1045
1046                         /* remove the region from the old playlist */
1047
1048                         insert_result = modified_playlists.insert (from_playlist);
1049
1050                         if (insert_result.second) {
1051                                 _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
1052                         }
1053
1054                         from_playlist->remove_region (rv->region());
1055
1056                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1057                            was selected in all of them, then removing it from a playlist will have removed all
1058                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
1059                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1060                            corresponding regionview, and the selection is now empty).
1061
1062                            this could have invalidated any and all iterators into the region selection.
1063
1064                            the heuristic we use here is: if the region selection is empty, break out of the loop
1065                            here. if the region selection is not empty, then restart the loop because we know that
1066                            we must have removed at least the region(view) we've just been working on as well as any
1067                            that we processed on previous iterations.
1068
1069                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
1070                            we can just iterate.
1071                         */
1072
1073                         if (_views.empty()) {
1074                                 break;
1075                         } else {
1076                                 i = _views.begin();
1077                         }
1078
1079                 } else {
1080                         ++i;
1081                 }
1082
1083                 if (_copy) {
1084                         copies.push_back (rv);
1085                 }
1086         }
1087         /*
1088            if we've created new regions either by copying or moving 
1089            to a new track, we want to replace the old selection with the new ones 
1090         */
1091         if (new_views.size() > 0) {
1092                 _editor->selection->set (new_views);
1093         }
1094
1095         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1096                 (*p)->thaw();
1097         }
1098
1099   out:
1100         for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
1101                 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
1102         }
1103
1104         _editor->commit_reversible_command ();
1105
1106         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
1107                 delete *x;
1108         }
1109 }
1110
1111 void
1112 RegionMoveDrag::aborted ()
1113 {
1114         if (_copy) {
1115
1116                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1117                         delete *i;
1118                 }
1119
1120                 _views.clear ();
1121
1122         } else {
1123                 RegionMotionDrag::aborted ();
1124         }
1125 }
1126
1127 void
1128 RegionMotionDrag::aborted ()
1129 {
1130         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1131                 TimeAxisView* tv = &(*i)->get_time_axis_view ();
1132                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1133                 assert (rtv);
1134                 (*i)->get_canvas_group()->reparent (*rtv->view()->canvas_item());
1135                 (*i)->get_canvas_group()->property_y() = 0;
1136                 (*i)->get_time_axis_view().reveal_dependent_views (**i);
1137                 (*i)->fake_set_opaque (false);
1138                 (*i)->move (-_total_x_delta, 0);
1139                 (*i)->set_height (rtv->view()->child_height ());
1140         }
1141
1142         _editor->update_canvas_now ();
1143 }
1144                                       
1145
1146 bool
1147 RegionMotionDrag::x_move_allowed () const
1148 {
1149         if (Config->get_edit_mode() == Lock) {
1150                 /* in locked edit mode, reverse the usual meaning of _x_constrained */
1151                 return _x_constrained;
1152         }
1153
1154         return !_x_constrained;
1155 }
1156
1157 void
1158 RegionMotionDrag::copy_regions (GdkEvent* event)
1159 {
1160         /* duplicate the regionview(s) and region(s) */
1161
1162         list<RegionView*> new_regionviews;
1163
1164         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1165
1166                 RegionView* rv = (*i);
1167                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1168                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1169
1170                 const boost::shared_ptr<const Region> original = rv->region();
1171                 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1172
1173                 RegionView* nrv;
1174                 if (arv) {
1175                         boost::shared_ptr<AudioRegion> audioregion_copy
1176                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1177                         nrv = new AudioRegionView (*arv, audioregion_copy);
1178                 } else if (mrv) {
1179                         boost::shared_ptr<MidiRegion> midiregion_copy
1180                                 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1181                         nrv = new MidiRegionView (*mrv, midiregion_copy);
1182                 } else {
1183                         continue;
1184                 }
1185
1186                 nrv->get_canvas_group()->show ();
1187                 new_regionviews.push_back (nrv);
1188
1189                 /* swap _primary to the copy */
1190
1191                 if (rv == _primary) {
1192                         _primary = nrv;
1193                 }
1194
1195                 /* ..and deselect the one we copied */
1196
1197                 rv->set_selected (false);
1198         }
1199
1200         if (new_regionviews.empty()) {
1201                 return;
1202         }
1203
1204         /* reflect the fact that we are dragging the copies */
1205
1206         _views = new_regionviews;
1207
1208         swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1209
1210         /*
1211            sync the canvas to what we think is its current state
1212            without it, the canvas seems to
1213            "forget" to update properly after the upcoming reparent()
1214            ..only if the mouse is in rapid motion at the time of the grab.
1215            something to do with regionview creation taking so long?
1216         */
1217         _editor->update_canvas_now();
1218 }
1219
1220 bool
1221 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1222 {
1223         /* Which trackview is this ? */
1224
1225         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1226         (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1227         (*layer) = tvp.second;
1228
1229         if (*tv && (*tv)->layer_display() == Overlaid) {
1230                 *layer = 0;
1231         }
1232
1233         /* The region motion is only processed if the pointer is over
1234            an audio track.
1235         */
1236
1237         if (!(*tv) || !(*tv)->is_track()) {
1238                 /* To make sure we hide the verbose canvas cursor when the mouse is
1239                    not held over and audiotrack.
1240                 */
1241                 _editor->hide_verbose_canvas_cursor ();
1242                 return false;
1243         }
1244
1245         return true;
1246 }
1247
1248 /** @param new_order New track order.
1249  *  @param old_order Old track order.
1250  *  @param visible_y_low Lowest visible order.
1251  *  @return true if y movement should not happen, otherwise false.
1252  */
1253 bool
1254 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1255 {
1256         if (new_order != old_order) {
1257
1258                 /* this isn't the pointer track */
1259
1260                 if (y_span > 0) {
1261
1262                         /* moving up the canvas */
1263                         if ( (new_order - y_span) >= tavs.visible_y_low) {
1264
1265                                 int32_t n = 0;
1266
1267                                 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1268                                 int32_t visible_tracks = 0;
1269                                 while (visible_tracks < y_span ) {
1270                                         visible_tracks++;
1271                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1272                                                 /* passing through a hidden track */
1273                                                 n--;
1274                                         }
1275                                 }
1276
1277                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1278                                         /* moving to a non-track; disallow */
1279                                         return true;
1280                                 }
1281
1282
1283                         } else {
1284                                 /* moving beyond the lowest visible track; disallow */
1285                                 return true;
1286                         }
1287
1288                 } else if (y_span < 0) {
1289
1290                         /* moving down the canvas */
1291                         if ((new_order - y_span) <= tavs.visible_y_high) {
1292
1293                                 int32_t visible_tracks = 0;
1294                                 int32_t n = 0;
1295                                 while (visible_tracks > y_span ) {
1296                                         visible_tracks--;
1297
1298                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1299                                                 /* passing through a hidden track */
1300                                                 n++;
1301                                         }
1302                                 }
1303
1304                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1305                                         /* moving to a non-track; disallow */
1306                                         return true;
1307                                 }
1308
1309
1310                         } else {
1311
1312                                 /* moving beyond the highest visible track; disallow */
1313                                 return true;
1314                         }
1315                 }
1316
1317         } else {
1318
1319                 /* this is the pointer's track */
1320
1321                 if ((new_order - y_span) > tavs.visible_y_high) {
1322                         /* we will overflow */
1323                         return true;
1324                 } else if ((new_order - y_span) < tavs.visible_y_low) {
1325                         /* we will overflow */
1326                         return true;
1327                 }
1328         }
1329
1330         return false;
1331 }
1332
1333
1334 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1335         : RegionMotionDrag (e, i, p, v, b),
1336           _copy (c)
1337 {
1338         TimeAxisView* const tv = &_primary->get_time_axis_view ();
1339
1340         _dest_trackview = tv;
1341         if (tv->layer_display() == Overlaid) {
1342                 _dest_layer = 0;
1343         } else {
1344                 _dest_layer = _primary->region()->layer ();
1345         }
1346
1347         double speed = 1;
1348         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1349         if (rtv && rtv->is_track()) {
1350                 speed = rtv->get_diskstream()->speed ();
1351         }
1352
1353         _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1354 }
1355
1356 void
1357 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1358 {
1359         RegionMotionDrag::start_grab (event, c);
1360
1361         _pointer_frame_offset = grab_frame() - _last_frame_position;
1362 }
1363
1364 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1365         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1366 {
1367         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1368                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1369
1370         _primary = v->view()->create_region_view (r, false, false);
1371
1372         _primary->get_canvas_group()->show ();
1373         _primary->set_position (pos, 0);
1374         _views.push_back (_primary);
1375
1376         _last_frame_position = pos;
1377
1378         _item = _primary->get_canvas_group ();
1379         _dest_trackview = v;
1380         _dest_layer = _primary->region()->layer ();
1381 }
1382
1383 map<RegionView*, pair<RouteTimeAxisView*, int> >
1384 RegionMotionDrag::find_time_axis_views_and_layers ()
1385 {
1386         map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1387
1388         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1389
1390                 double ix1, ix2, iy1, iy2;
1391                 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1392                 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1393                 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1394
1395                 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1396                 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1397         }
1398
1399         return tav;
1400 }
1401
1402
1403 void
1404 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1405 {
1406         _editor->update_canvas_now ();
1407
1408         map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1409
1410         RouteTimeAxisView* dest_rtv = final[_primary].first;
1411
1412         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1413         _primary->get_canvas_group()->property_y() = 0;
1414
1415         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1416
1417         _editor->begin_reversible_command (_("insert region"));
1418         XMLNode& before = playlist->get_state ();
1419         playlist->add_region (_primary->region (), _last_frame_position);
1420         _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1421         _editor->commit_reversible_command ();
1422
1423         delete _primary;
1424         _primary = 0;
1425         _views.clear ();
1426 }
1427
1428 void
1429 RegionInsertDrag::aborted ()
1430 {
1431         /* XXX: TODO */
1432 }
1433
1434 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1435         : RegionMoveDrag (e, i, p, v, false, false)
1436 {
1437
1438 }
1439
1440 struct RegionSelectionByPosition {
1441     bool operator() (RegionView*a, RegionView* b) {
1442             return a->region()->position () < b->region()->position();
1443     }
1444 };
1445
1446 void
1447 RegionSpliceDrag::motion (GdkEvent* event, bool)
1448 {
1449         RouteTimeAxisView* tv;
1450         layer_t layer;
1451
1452         if (!check_possible (&tv, &layer)) {
1453                 return;
1454         }
1455
1456         int dir;
1457
1458         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1459                 dir = 1;
1460         } else {
1461                 dir = -1;
1462         }
1463
1464         RegionSelection copy (_editor->selection->regions);
1465
1466         RegionSelectionByPosition cmp;
1467         copy.sort (cmp);
1468
1469         nframes64_t const pf = adjusted_current_frame (event);
1470
1471         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1472
1473                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1474
1475                 if (!atv) {
1476                         continue;
1477                 }
1478
1479                 boost::shared_ptr<Playlist> playlist;
1480
1481                 if ((playlist = atv->playlist()) == 0) {
1482                         continue;
1483                 }
1484
1485                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1486                         continue;
1487                 }
1488
1489                 if (dir > 0) {
1490                         if (pf < (*i)->region()->last_frame() + 1) {
1491                                 continue;
1492                         }
1493                 } else {
1494                         if (pf > (*i)->region()->first_frame()) {
1495                                 continue;
1496                         }
1497                 }
1498
1499
1500                 playlist->shuffle ((*i)->region(), dir);
1501         }
1502 }
1503
1504 void
1505 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1506 {
1507
1508 }
1509
1510 void
1511 RegionSpliceDrag::aborted ()
1512 {
1513         /* XXX: TODO */
1514 }
1515
1516 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1517         : Drag (e, i),
1518           _view (v)
1519 {
1520
1521 }
1522
1523 void
1524 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1525 {
1526         _dest_trackview = _view;
1527
1528         Drag::start_grab (event);
1529 }
1530
1531
1532 void
1533 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1534 {
1535         if (first_move) {
1536                 // TODO: create region-create-drag region view here
1537         }
1538
1539         // TODO: resize region-create-drag region view here
1540 }
1541
1542 void
1543 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1544 {
1545         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1546
1547         if (!mtv) {
1548                 return;
1549         }
1550
1551         if (!movement_occurred) {
1552                 mtv->add_region (grab_frame ());
1553         } else {
1554                 motion (event, false);
1555                 // TODO: create region-create-drag region here
1556         }
1557 }
1558
1559 void
1560 RegionCreateDrag::aborted ()
1561 {
1562         /* XXX: TODO */
1563 }
1564
1565 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1566         : Drag (e, i)
1567         , region (0)
1568 {
1569
1570 }
1571
1572 void
1573 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1574 {
1575         Gdk::Cursor     cursor;
1576         ArdourCanvas::CanvasNote*     cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1577
1578         Drag::start_grab (event);
1579
1580         region = &cnote->region_view();
1581
1582         double region_start = region->get_position_pixels();
1583         double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1584
1585         if (grab_x() <= middle_point) {
1586                 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1587                 at_front = true;
1588         } else {
1589                 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1590                 at_front = false;
1591         }
1592
1593         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1594
1595         if (event->motion.state & Keyboard::PrimaryModifier) {
1596                 relative = false;
1597         } else {
1598                 relative = true;
1599         }
1600
1601         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1602
1603         if (ms.size() > 1) {
1604                 /* has to be relative, may make no sense otherwise */
1605                 relative = true;
1606         }
1607
1608         region->note_selected (cnote, true);
1609
1610         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1611                 MidiRegionSelection::iterator next;
1612                 next = r;
1613                 ++next;
1614                 (*r)->begin_resizing (at_front);
1615                 r = next;
1616         }
1617 }
1618
1619 void
1620 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1621 {
1622         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1623         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1624                 (*r)->update_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1625         }
1626 }
1627
1628 void
1629 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1630 {
1631         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1632         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1633                 (*r)->commit_resizing (at_front, _drags->current_pointer_x() - grab_x(), relative);
1634         }
1635 }
1636
1637 void
1638 NoteResizeDrag::aborted ()
1639 {
1640         /* XXX: TODO */
1641 }
1642
1643 void
1644 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1645 {
1646
1647 }
1648
1649 void
1650 RegionGainDrag::finished (GdkEvent *, bool)
1651 {
1652
1653 }
1654
1655 void
1656 RegionGainDrag::aborted ()
1657 {
1658         /* XXX: TODO */
1659 }
1660
1661 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1662         : RegionDrag (e, i, p, v)
1663         , _have_transaction (false)
1664 {
1665
1666 }
1667
1668 void
1669 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1670 {
1671         double speed = 1.0;
1672         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1673         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1674
1675         if (tv && tv->is_track()) {
1676                 speed = tv->get_diskstream()->speed();
1677         }
1678
1679         nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1680         nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1681         nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1682
1683         Drag::start_grab (event, _editor->trimmer_cursor);
1684
1685         nframes64_t const pf = adjusted_current_frame (event);
1686
1687         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1688                 _operation = ContentsTrim;
1689         } else {
1690                 /* These will get overridden for a point trim.*/
1691                 if (pf < (region_start + region_length/2)) {
1692                         /* closer to start */
1693                         _operation = StartTrim;
1694                 } else if (pf > (region_end - region_length/2)) {
1695                         /* closer to end */
1696                         _operation = EndTrim;
1697                 }
1698         }
1699
1700         switch (_operation) {
1701         case StartTrim:
1702                 _editor->show_verbose_time_cursor (region_start, 10);
1703                 break;
1704         case EndTrim:
1705                 _editor->show_verbose_time_cursor (region_end, 10);
1706                 break;
1707         case ContentsTrim:
1708                 _editor->show_verbose_time_cursor (pf, 10);
1709                 break;
1710         }
1711 }
1712
1713 void
1714 TrimDrag::motion (GdkEvent* event, bool first_move)
1715 {
1716         RegionView* rv = _primary;
1717         nframes64_t frame_delta = 0;
1718
1719         bool left_direction;
1720         bool obey_snap = event ? !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier()) : false;
1721
1722         /* snap modifier works differently here..
1723            its current state has to be passed to the
1724            various trim functions in order to work properly
1725         */
1726
1727         double speed = 1.0;
1728         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1729         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1730         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1731
1732         if (tv && tv->is_track()) {
1733                 speed = tv->get_diskstream()->speed();
1734         }
1735
1736         nframes64_t const pf = adjusted_current_frame (event);
1737
1738         if (last_pointer_frame() > pf) {
1739                 left_direction = true;
1740         } else {
1741                 left_direction = false;
1742         }
1743
1744         if (first_move) {
1745
1746                 string trim_type;
1747
1748                 switch (_operation) {
1749                 case StartTrim:
1750                         trim_type = "Region start trim";
1751                         break;
1752                 case EndTrim:
1753                         trim_type = "Region end trim";
1754                         break;
1755                 case ContentsTrim:
1756                         trim_type = "Region content trim";
1757                         break;
1758                 }
1759
1760                 _editor->begin_reversible_command (trim_type);
1761                 _have_transaction = true;
1762
1763                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1764                         (*i)->fake_set_opaque(false);
1765                         (*i)->region()->freeze ();
1766
1767                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1768
1769                         if (arv){
1770                                 arv->temporarily_hide_envelope ();
1771                         }
1772
1773                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1774                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1775
1776                         if (insert_result.second) {
1777                                 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1778                                 pl->freeze();
1779                         }
1780                 }
1781         }
1782
1783         if (left_direction) {
1784                 frame_delta = (last_pointer_frame() - pf);
1785         } else {
1786                 frame_delta = (pf - last_pointer_frame());
1787         }
1788
1789         bool non_overlap_trim = false;
1790
1791         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1792                 non_overlap_trim = true;
1793         }
1794
1795         switch (_operation) {
1796         case StartTrim:
1797                 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1798                         break;
1799                 } else {
1800
1801                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1802                                 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1803                         }
1804                         break;
1805                 }
1806
1807         case EndTrim:
1808                 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1809                         break;
1810                 } else {
1811
1812                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1813                                 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1814                         }
1815                         break;
1816                 }
1817
1818         case ContentsTrim:
1819                 {
1820                         bool swap_direction = false;
1821
1822                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1823                                 swap_direction = true;
1824                         }
1825
1826                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1827                                 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1828                         }
1829                 }
1830                 break;
1831         }
1832
1833         switch (_operation) {
1834         case StartTrim:
1835                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1836                 break;
1837         case EndTrim:
1838                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1839                 break;
1840         case ContentsTrim:
1841                 _editor->show_verbose_time_cursor (pf, 10);
1842                 break;
1843         }
1844 }
1845
1846
1847 void
1848 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1849 {
1850         if (movement_occurred) {
1851                 motion (event, false);
1852
1853                 if (!_editor->selection->selected (_primary)) {
1854                         _editor->thaw_region_after_trim (*_primary);
1855                 } else {
1856
1857                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1858                                 _editor->thaw_region_after_trim (**i);
1859                                 (*i)->fake_set_opaque (true);
1860                         }
1861                 }
1862                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1863                         (*p)->thaw ();
1864                         if (_have_transaction) {
1865                                 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1866                         }
1867                 }
1868
1869                 _editor->motion_frozen_playlists.clear ();
1870
1871                 if (_have_transaction) {
1872                         _editor->commit_reversible_command();
1873                 }
1874
1875         } else {
1876                 /* no mouse movement */
1877                 _editor->point_trim (event, adjusted_current_frame (event));
1878         }
1879 }
1880
1881 void
1882 TrimDrag::aborted ()
1883 {
1884         /* Our motion method is changing model state, so use the Undo system
1885            to cancel.  Perhaps not ideal, as this will leave an Undo point
1886            behind which may be slightly odd from the user's point of view.
1887         */
1888
1889         finished (0, true);
1890         
1891         if (_have_transaction) {
1892                 _editor->undo ();
1893         }
1894 }
1895
1896 pair<nframes64_t, nframes64_t>
1897 TrimDrag::extent () const
1898 {
1899         /* we only want to autoscroll to keep the most `outward' region edge on-screen,
1900            not the whole region(s) that is/are being trimmed.
1901         */
1902
1903         nframes64_t f = 0;
1904
1905         switch (_operation) {
1906         case StartTrim:
1907                 f = INT64_MAX;
1908                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1909                         f = min (f, (nframes64_t) (*i)->region()->position());
1910                 }
1911                 break; 
1912         case ContentsTrim:
1913                 f = adjusted_current_frame (0);
1914                 break;
1915         case EndTrim:
1916                 f = 0;
1917                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1918                         f = max (f, (nframes64_t) (*i)->region()->position() + (*i)->region()->length());
1919                 }
1920                 break;
1921         }
1922                 
1923         return make_pair (f, f);
1924 }
1925
1926
1927 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1928         : Drag (e, i),
1929           _copy (c)
1930 {
1931         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1932         assert (_marker);
1933 }
1934
1935 void
1936 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1937 {
1938         if (_copy) {
1939                 // create a dummy marker for visual representation of moving the copy.
1940                 // The actual copying is not done before we reach the finish callback.
1941                 char name[64];
1942                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1943                 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1944                                                           *new MeterSection (_marker->meter()));
1945
1946                 _item = &new_marker->the_item ();
1947                 _marker = new_marker;
1948
1949         } else {
1950
1951                 MetricSection& section (_marker->meter());
1952
1953                 if (!section.movable()) {
1954                         return;
1955                 }
1956
1957         }
1958
1959         Drag::start_grab (event, cursor);
1960
1961         _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1962
1963         _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1964 }
1965
1966 void
1967 MeterMarkerDrag::motion (GdkEvent* event, bool)
1968 {
1969         nframes64_t const pf = adjusted_current_frame (event);
1970
1971         _marker->set_position (pf);
1972         
1973         _editor->show_verbose_time_cursor (pf, 10);
1974 }
1975
1976 void
1977 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1978 {
1979         if (!movement_occurred) {
1980                 return;
1981         }
1982
1983         motion (event, false);
1984
1985         BBT_Time when;
1986
1987         TempoMap& map (_editor->session()->tempo_map());
1988         map.bbt_time (last_pointer_frame(), when);
1989
1990         if (_copy == true) {
1991                 _editor->begin_reversible_command (_("copy meter mark"));
1992                 XMLNode &before = map.get_state();
1993                 map.add_meter (_marker->meter(), when);
1994                 XMLNode &after = map.get_state();
1995                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1996                 _editor->commit_reversible_command ();
1997
1998                 // delete the dummy marker we used for visual representation of copying.
1999                 // a new visual marker will show up automatically.
2000                 delete _marker;
2001         } else {
2002                 _editor->begin_reversible_command (_("move meter mark"));
2003                 XMLNode &before = map.get_state();
2004                 map.move_meter (_marker->meter(), when);
2005                 XMLNode &after = map.get_state();
2006                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2007                 _editor->commit_reversible_command ();
2008         }
2009 }
2010
2011 void
2012 MeterMarkerDrag::aborted ()
2013 {
2014         _marker->set_position (_marker->meter().frame ());
2015 }
2016
2017 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2018         : Drag (e, i),
2019           _copy (c)
2020 {
2021         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2022         assert (_marker);
2023 }
2024
2025 void
2026 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2027 {
2028
2029         if (_copy) {
2030
2031                 // create a dummy marker for visual representation of moving the copy.
2032                 // The actual copying is not done before we reach the finish callback.
2033                 char name[64];
2034                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2035                 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
2036                                                           *new TempoSection (_marker->tempo()));
2037
2038                 _item = &new_marker->the_item ();
2039                 _marker = new_marker;
2040
2041         } else {
2042
2043                 MetricSection& section (_marker->tempo());
2044
2045                 if (!section.movable()) {
2046                         return;
2047                 }
2048         }
2049
2050         Drag::start_grab (event, cursor);
2051
2052         _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
2053         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2054 }
2055
2056 void
2057 TempoMarkerDrag::motion (GdkEvent* event, bool)
2058 {
2059         nframes64_t const pf = adjusted_current_frame (event);
2060         _marker->set_position (pf);
2061         _editor->show_verbose_time_cursor (pf, 10);
2062 }
2063
2064 void
2065 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2066 {
2067         if (!movement_occurred) {
2068                 return;
2069         }
2070
2071         motion (event, false);
2072
2073         BBT_Time when;
2074
2075         TempoMap& map (_editor->session()->tempo_map());
2076         map.bbt_time (last_pointer_frame(), when);
2077
2078         if (_copy == true) {
2079                 _editor->begin_reversible_command (_("copy tempo mark"));
2080                 XMLNode &before = map.get_state();
2081                 map.add_tempo (_marker->tempo(), when);
2082                 XMLNode &after = map.get_state();
2083                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2084                 _editor->commit_reversible_command ();
2085
2086                 // delete the dummy marker we used for visual representation of copying.
2087                 // a new visual marker will show up automatically.
2088                 delete _marker;
2089         } else {
2090                 _editor->begin_reversible_command (_("move tempo mark"));
2091                 XMLNode &before = map.get_state();
2092                 map.move_tempo (_marker->tempo(), when);
2093                 XMLNode &after = map.get_state();
2094                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2095                 _editor->commit_reversible_command ();
2096         }
2097 }
2098
2099 void
2100 TempoMarkerDrag::aborted ()
2101 {
2102         _marker->set_position (_marker->tempo().frame());
2103 }
2104
2105 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
2106         : Drag (e, i),
2107           _stop (s)
2108 {
2109         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
2110         assert (_cursor);
2111 }
2112
2113 void
2114 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2115 {
2116         Drag::start_grab (event, c);
2117
2118         if (!_stop) {
2119
2120                 nframes64_t where = _editor->event_frame (event, 0, 0);
2121
2122                 _editor->snap_to_with_modifier (where, event);
2123                 _editor->playhead_cursor->set_position (where);
2124
2125         }
2126
2127         if (_cursor == _editor->playhead_cursor) {
2128                 _editor->_dragging_playhead = true;
2129
2130                 if (_editor->session() && _was_rolling && _stop) {
2131                         _editor->session()->request_stop ();
2132                 }
2133
2134                 if (_editor->session() && _editor->session()->is_auditioning()) {
2135                         _editor->session()->cancel_audition ();
2136                 }
2137         }
2138
2139         _pointer_frame_offset = grab_frame() - _cursor->current_frame;
2140
2141         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2142 }
2143
2144 void
2145 CursorDrag::motion (GdkEvent* event, bool)
2146 {
2147         nframes64_t const adjusted_frame = adjusted_current_frame (event);
2148
2149         if (adjusted_frame == last_pointer_frame()) {
2150                 return;
2151         }
2152
2153         _cursor->set_position (adjusted_frame);
2154
2155         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
2156
2157 #ifdef GTKOSX
2158         _editor->update_canvas_now ();
2159 #endif
2160         _editor->UpdateAllTransportClocks (_cursor->current_frame);
2161 }
2162
2163 void
2164 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2165 {
2166         _editor->_dragging_playhead = false;
2167
2168         if (!movement_occurred && _stop) {
2169                 return;
2170         }
2171
2172         motion (event, false);
2173
2174         if (_item == &_editor->playhead_cursor->canvas_item) {
2175                 if (_editor->session()) {
2176                         _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2177                         _editor->_pending_locate_request = true;
2178                 }
2179         }
2180 }
2181
2182 void
2183 CursorDrag::aborted ()
2184 {
2185         _editor->_dragging_playhead = false;
2186         _cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2187 }
2188
2189 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2190         : RegionDrag (e, i, p, v)
2191 {
2192
2193 }
2194
2195 void
2196 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2197 {
2198         Drag::start_grab (event, cursor);
2199
2200         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2201         boost::shared_ptr<AudioRegion> const r = a->audio_region ();
2202
2203         _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
2204         _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2205 }
2206
2207 void
2208 FadeInDrag::motion (GdkEvent* event, bool)
2209 {
2210         nframes64_t fade_length;
2211
2212         nframes64_t const pos = adjusted_current_frame (event);
2213
2214         boost::shared_ptr<Region> region = _primary->region ();
2215
2216         if (pos < (region->position() + 64)) {
2217                 fade_length = 64; // this should be a minimum defined somewhere
2218         } else if (pos > region->last_frame()) {
2219                 fade_length = region->length();
2220         } else {
2221                 fade_length = pos - region->position();
2222         }
2223
2224         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2225
2226                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2227
2228                 if (!tmp) {
2229                         continue;
2230                 }
2231
2232                 tmp->reset_fade_in_shape_width (fade_length);
2233         }
2234
2235         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2236 }
2237
2238 void
2239 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2240 {
2241         if (!movement_occurred) {
2242                 return;
2243         }
2244
2245         nframes64_t fade_length;
2246
2247         nframes64_t const pos = adjusted_current_frame (event);
2248
2249         boost::shared_ptr<Region> region = _primary->region ();
2250
2251         if (pos < (region->position() + 64)) {
2252                 fade_length = 64; // this should be a minimum defined somewhere
2253         } else if (pos > region->last_frame()) {
2254                 fade_length = region->length();
2255         } else {
2256                 fade_length = pos - region->position();
2257         }
2258
2259         _editor->begin_reversible_command (_("change fade in length"));
2260
2261         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2262
2263                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2264
2265                 if (!tmp) {
2266                         continue;
2267                 }
2268
2269                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2270                 XMLNode &before = alist->get_state();
2271
2272                 tmp->audio_region()->set_fade_in_length (fade_length);
2273                 tmp->audio_region()->set_fade_in_active (true);
2274
2275                 XMLNode &after = alist->get_state();
2276                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2277         }
2278
2279         _editor->commit_reversible_command ();
2280 }
2281
2282 void
2283 FadeInDrag::aborted ()
2284 {
2285         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2286                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2287
2288                 if (!tmp) {
2289                         continue;
2290                 }
2291
2292                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2293         }
2294 }
2295
2296 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2297         : RegionDrag (e, i, p, v)
2298 {
2299
2300 }
2301
2302 void
2303 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2304 {
2305         Drag::start_grab (event, cursor);
2306
2307         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2308         boost::shared_ptr<AudioRegion> r = a->audio_region ();
2309
2310         _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2311         _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2312 }
2313
2314 void
2315 FadeOutDrag::motion (GdkEvent* event, bool)
2316 {
2317         nframes64_t fade_length;
2318
2319         nframes64_t const pos = adjusted_current_frame (event);
2320
2321         boost::shared_ptr<Region> region = _primary->region ();
2322
2323         if (pos > (region->last_frame() - 64)) {
2324                 fade_length = 64; // this should really be a minimum fade defined somewhere
2325         }
2326         else if (pos < region->position()) {
2327                 fade_length = region->length();
2328         }
2329         else {
2330                 fade_length = region->last_frame() - pos;
2331         }
2332
2333         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2334
2335                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2336
2337                 if (!tmp) {
2338                         continue;
2339                 }
2340
2341                 tmp->reset_fade_out_shape_width (fade_length);
2342         }
2343
2344         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2345 }
2346
2347 void
2348 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2349 {
2350         if (!movement_occurred) {
2351                 return;
2352         }
2353
2354         nframes64_t fade_length;
2355
2356         nframes64_t const pos = adjusted_current_frame (event);
2357
2358         boost::shared_ptr<Region> region = _primary->region ();
2359
2360         if (pos > (region->last_frame() - 64)) {
2361                 fade_length = 64; // this should really be a minimum fade defined somewhere
2362         }
2363         else if (pos < region->position()) {
2364                 fade_length = region->length();
2365         }
2366         else {
2367                 fade_length = region->last_frame() - pos;
2368         }
2369
2370         _editor->begin_reversible_command (_("change fade out length"));
2371
2372         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2373
2374                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2375
2376                 if (!tmp) {
2377                         continue;
2378                 }
2379
2380                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2381                 XMLNode &before = alist->get_state();
2382
2383                 tmp->audio_region()->set_fade_out_length (fade_length);
2384                 tmp->audio_region()->set_fade_out_active (true);
2385
2386                 XMLNode &after = alist->get_state();
2387                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2388         }
2389
2390         _editor->commit_reversible_command ();
2391 }
2392
2393 void
2394 FadeOutDrag::aborted ()
2395 {
2396         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2397                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2398
2399                 if (!tmp) {
2400                         continue;
2401                 }
2402
2403                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2404         }
2405 }
2406
2407 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2408         : Drag (e, i)
2409 {
2410         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2411         assert (_marker);
2412
2413         _points.push_back (Gnome::Art::Point (0, 0));
2414         _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2415
2416         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2417         _line->property_width_pixels() = 1;
2418         _line->property_points () = _points;
2419         _line->hide ();
2420
2421         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2422 }
2423
2424 MarkerDrag::~MarkerDrag ()
2425 {
2426         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2427                 delete *i;
2428         }
2429 }
2430
2431 void
2432 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2433 {
2434         Drag::start_grab (event, cursor);
2435
2436         bool is_start;
2437
2438         Location *location = _editor->find_location_from_marker (_marker, is_start);
2439         _editor->_dragging_edit_point = true;
2440
2441         _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2442
2443         update_item (location);
2444
2445         // _drag_line->show();
2446         // _line->raise_to_top();
2447
2448         if (is_start) {
2449                 _editor->show_verbose_time_cursor (location->start(), 10);
2450         } else {
2451                 _editor->show_verbose_time_cursor (location->end(), 10);
2452         }
2453
2454         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2455
2456         switch (op) {
2457         case Selection::Toggle:
2458                 _editor->selection->toggle (_marker);
2459                 break;
2460         case Selection::Set:
2461                 if (!_editor->selection->selected (_marker)) {
2462                         _editor->selection->set (_marker);
2463                 }
2464                 break;
2465         case Selection::Extend:
2466         {
2467                 Locations::LocationList ll;
2468                 list<Marker*> to_add;
2469                 nframes64_t s, e;
2470                 _editor->selection->markers.range (s, e);
2471                 s = min (_marker->position(), s);
2472                 e = max (_marker->position(), e);
2473                 s = min (s, e);
2474                 e = max (s, e);
2475                 if (e < max_frames) {
2476                         ++e;
2477                 }
2478                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2479                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2480                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2481                         if (lm) {
2482                                 if (lm->start) {
2483                                         to_add.push_back (lm->start);
2484                                 }
2485                                 if (lm->end) {
2486                                         to_add.push_back (lm->end);
2487                                 }
2488                         }
2489                 }
2490                 if (!to_add.empty()) {
2491                         _editor->selection->add (to_add);
2492                 }
2493                 break;
2494         }
2495         case Selection::Add:
2496                 _editor->selection->add (_marker);
2497                 break;
2498         }
2499
2500         /* set up copies for us to manipulate during the drag */
2501
2502         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2503                 Location *l = _editor->find_location_from_marker (*i, is_start);
2504                 _copied_locations.push_back (new Location (*l));
2505         }
2506 }
2507
2508 void
2509 MarkerDrag::motion (GdkEvent* event, bool)
2510 {
2511         nframes64_t f_delta = 0;
2512         bool is_start;
2513         bool move_both = false;
2514         Marker* marker;
2515         Location  *real_location;
2516         Location *copy_location = 0;
2517
2518         nframes64_t const newframe = adjusted_current_frame (event);
2519
2520         nframes64_t next = newframe;
2521
2522         if (newframe == last_pointer_frame()) {
2523                 return;
2524         }
2525
2526         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2527                 move_both = true;
2528         }
2529
2530         MarkerSelection::iterator i;
2531         list<Location*>::iterator x;
2532
2533         /* find the marker we're dragging, and compute the delta */
2534
2535         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2536              x != _copied_locations.end() && i != _editor->selection->markers.end();
2537              ++i, ++x) {
2538
2539                 copy_location = *x;
2540                 marker = *i;
2541
2542                 if (marker == _marker) {
2543
2544                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2545                                 /* que pasa ?? */
2546                                 return;
2547                         }
2548
2549                         if (real_location->is_mark()) {
2550                                 f_delta = newframe - copy_location->start();
2551                         } else {
2552
2553
2554                                 switch (marker->type()) {
2555                                 case Marker::Start:
2556                                 case Marker::LoopStart:
2557                                 case Marker::PunchIn:
2558                                         f_delta = newframe - copy_location->start();
2559                                         break;
2560
2561                                 case Marker::End:
2562                                 case Marker::LoopEnd:
2563                                 case Marker::PunchOut:
2564                                         f_delta = newframe - copy_location->end();
2565                                         break;
2566                                 default:
2567                                         /* what kind of marker is this ? */
2568                                         return;
2569                                 }
2570                         }
2571                         break;
2572                 }
2573         }
2574
2575         if (i == _editor->selection->markers.end()) {
2576                 /* hmm, impossible - we didn't find the dragged marker */
2577                 return;
2578         }
2579
2580         /* now move them all */
2581
2582         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2583              x != _copied_locations.end() && i != _editor->selection->markers.end();
2584              ++i, ++x) {
2585
2586                 copy_location = *x;
2587                 marker = *i;
2588
2589                 /* call this to find out if its the start or end */
2590
2591                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2592                         continue;
2593                 }
2594
2595                 if (real_location->locked()) {
2596                         continue;
2597                 }
2598
2599                 if (copy_location->is_mark()) {
2600
2601                         /* just move it */
2602
2603                         copy_location->set_start (copy_location->start() + f_delta);
2604
2605                 } else {
2606
2607                         nframes64_t new_start = copy_location->start() + f_delta;
2608                         nframes64_t new_end = copy_location->end() + f_delta;
2609
2610                         if (is_start) { // start-of-range marker
2611
2612                                 if (move_both) {
2613                                         copy_location->set_start (new_start);
2614                                         copy_location->set_end (new_end);
2615                                 } else  if (new_start < copy_location->end()) {
2616                                         copy_location->set_start (new_start);
2617                                 } else {
2618                                         _editor->snap_to (next, 1, true);
2619                                         copy_location->set_end (next);
2620                                         copy_location->set_start (newframe);
2621                                 }
2622
2623                         } else { // end marker
2624
2625                                 if (move_both) {
2626                                         copy_location->set_end (new_end);
2627                                         copy_location->set_start (new_start);
2628                                 } else if (new_end > copy_location->start()) {
2629                                         copy_location->set_end (new_end);
2630                                 } else if (newframe > 0) {
2631                                         _editor->snap_to (next, -1, true);
2632                                         copy_location->set_start (next);
2633                                         copy_location->set_end (newframe);
2634                                 }
2635                         }
2636                 }
2637
2638                 update_item (copy_location);
2639
2640                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2641
2642                 if (lm) {
2643                         lm->set_position (copy_location->start(), copy_location->end());
2644                 }
2645         }
2646
2647         assert (!_copied_locations.empty());
2648
2649         _editor->show_verbose_time_cursor (newframe, 10);
2650
2651 #ifdef GTKOSX
2652         _editor->update_canvas_now ();
2653 #endif
2654 }
2655
2656 void
2657 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2658 {
2659         if (!movement_occurred) {
2660
2661                 /* just a click, do nothing but finish
2662                    off the selection process
2663                 */
2664
2665                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2666
2667                 switch (op) {
2668                 case Selection::Set:
2669                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2670                                 _editor->selection->set (_marker);
2671                         }
2672                         break;
2673
2674                 case Selection::Toggle:
2675                 case Selection::Extend:
2676                 case Selection::Add:
2677                         break;
2678                 }
2679
2680                 return;
2681         }
2682
2683         _editor->_dragging_edit_point = false;
2684
2685         _editor->begin_reversible_command ( _("move marker") );
2686         XMLNode &before = _editor->session()->locations()->get_state();
2687
2688         MarkerSelection::iterator i;
2689         list<Location*>::iterator x;
2690         bool is_start;
2691
2692         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2693              x != _copied_locations.end() && i != _editor->selection->markers.end();
2694              ++i, ++x) {
2695
2696                 Location * location = _editor->find_location_from_marker (*i, is_start);
2697
2698                 if (location) {
2699
2700                         if (location->locked()) {
2701                                 return;
2702                         }
2703
2704                         if (location->is_mark()) {
2705                                 location->set_start ((*x)->start());
2706                         } else {
2707                                 location->set ((*x)->start(), (*x)->end());
2708                         }
2709                 }
2710         }
2711
2712         XMLNode &after = _editor->session()->locations()->get_state();
2713         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2714         _editor->commit_reversible_command ();
2715
2716         _line->hide();
2717 }
2718
2719 void
2720 MarkerDrag::aborted ()
2721 {
2722         /* XXX: TODO */
2723 }
2724
2725 void
2726 MarkerDrag::update_item (Location* location)
2727 {
2728         double const x1 = _editor->frame_to_pixel (location->start());
2729
2730         _points.front().set_x(x1);
2731         _points.back().set_x(x1);
2732         _line->property_points() = _points;
2733 }
2734
2735 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2736         : Drag (e, i),
2737           _cumulative_x_drag (0),
2738           _cumulative_y_drag (0)
2739 {
2740         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2741         assert (_point);
2742 }
2743
2744
2745 void
2746 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2747 {
2748         Drag::start_grab (event, _editor->fader_cursor);
2749
2750         // start the grab at the center of the control point so
2751         // the point doesn't 'jump' to the mouse after the first drag
2752         _time_axis_view_grab_x = _point->get_x();
2753         _time_axis_view_grab_y = _point->get_y();
2754
2755         float const fraction = 1 - (_point->get_y() / _point->line().height());
2756
2757         _point->line().start_drag_single (_point, _time_axis_view_grab_x, fraction);
2758
2759         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2760                                             event->button.x + 10, event->button.y + 10);
2761
2762         _editor->show_verbose_canvas_cursor ();
2763 }
2764
2765 void
2766 ControlPointDrag::motion (GdkEvent* event, bool)
2767 {
2768         double dx = _drags->current_pointer_x() - last_pointer_x();
2769         double dy = _drags->current_pointer_y() - last_pointer_y();
2770
2771         if (event->button.state & Keyboard::SecondaryModifier) {
2772                 dx *= 0.1;
2773                 dy *= 0.1;
2774         }
2775
2776         /* coordinate in TimeAxisView's space */
2777         double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2778         double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2779
2780         // calculate zero crossing point. back off by .01 to stay on the
2781         // positive side of zero
2782         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2783
2784         // make sure we hit zero when passing through
2785         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2786                 cy = zero_gain_y;
2787         }
2788
2789         if (_x_constrained) {
2790                 cx = _time_axis_view_grab_x;
2791         }
2792         if (_y_constrained) {
2793                 cy = _time_axis_view_grab_y;
2794         }
2795
2796         _cumulative_x_drag = cx - _time_axis_view_grab_x;
2797         _cumulative_y_drag = cy - _time_axis_view_grab_y;
2798
2799         cx = max (0.0, cx);
2800         cy = max (0.0, cy);
2801         cy = min ((double) _point->line().height(), cy);
2802
2803         nframes64_t cx_frames = _editor->unit_to_frame (cx);
2804
2805         if (!_x_constrained) {
2806                 _editor->snap_to_with_modifier (cx_frames, event);
2807         }
2808
2809         float const fraction = 1.0 - (cy / _point->line().height());
2810
2811         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2812
2813         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2814
2815         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2816 }
2817
2818 void
2819 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2820 {
2821         if (!movement_occurred) {
2822
2823                 /* just a click */
2824
2825                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2826                         _editor->reset_point_selection ();
2827                 }
2828
2829         } else {
2830                 motion (event, false);
2831         }
2832         _point->line().end_drag ();
2833 }
2834
2835 void
2836 ControlPointDrag::aborted ()
2837 {
2838         _point->line().reset ();
2839 }
2840
2841 bool
2842 ControlPointDrag::active (Editing::MouseMode m)
2843 {
2844         if (m == Editing::MouseGain) {
2845                 /* always active in mouse gain */
2846                 return true;
2847         }
2848
2849         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2850         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2851 }
2852
2853 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2854         : Drag (e, i),
2855           _line (0),
2856           _cumulative_y_drag (0)
2857 {
2858
2859 }
2860 void
2861 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2862 {
2863         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2864         assert (_line);
2865
2866         _item = &_line->grab_item ();
2867
2868         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2869            origin, and ditto for y.
2870         */
2871
2872         double cx = event->button.x;
2873         double cy = event->button.y;
2874
2875         _line->parent_group().w2i (cx, cy);
2876
2877         nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2878
2879         uint32_t before;
2880         uint32_t after;
2881         
2882         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2883                 /* no adjacent points */
2884                 return;
2885         }
2886
2887         Drag::start_grab (event, _editor->fader_cursor);
2888
2889         /* store grab start in parent frame */
2890
2891         _time_axis_view_grab_x = cx;
2892         _time_axis_view_grab_y = cy;
2893
2894         double fraction = 1.0 - (cy / _line->height());
2895
2896         _line->start_drag_line (before, after, fraction);
2897
2898         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2899                                             event->button.x + 10, event->button.y + 10);
2900
2901         _editor->show_verbose_canvas_cursor ();
2902 }
2903
2904 void
2905 LineDrag::motion (GdkEvent* event, bool)
2906 {
2907         double dy = _drags->current_pointer_y() - last_pointer_y();
2908
2909         if (event->button.state & Keyboard::SecondaryModifier) {
2910                 dy *= 0.1;
2911         }
2912
2913         double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2914
2915         _cumulative_y_drag = cy - _time_axis_view_grab_y;
2916
2917         cy = max (0.0, cy);
2918         cy = min ((double) _line->height(), cy);
2919
2920         double const fraction = 1.0 - (cy / _line->height());
2921
2922         bool push;
2923
2924         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2925                 push = false;
2926         } else {
2927                 push = true;
2928         }
2929
2930         /* we are ignoring x position for this drag, so we can just pass in anything */
2931         _line->drag_motion (0, fraction, true, push);
2932
2933         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2934 }
2935
2936 void
2937 LineDrag::finished (GdkEvent* event, bool)
2938 {
2939         motion (event, false);
2940         _line->end_drag ();
2941 }
2942
2943 void
2944 LineDrag::aborted ()
2945 {
2946         _line->reset ();
2947 }
2948
2949 void
2950 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2951 {
2952         Drag::start_grab (event);
2953         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2954 }
2955
2956 void
2957 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2958 {
2959         nframes64_t start;
2960         nframes64_t end;
2961         double y1;
2962         double y2;
2963
2964         nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2965
2966         nframes64_t grab = grab_frame ();
2967         if (Config->get_rubberbanding_snaps_to_grid ()) {
2968                 _editor->snap_to_with_modifier (grab, event);
2969         }
2970
2971         /* base start and end on initial click position */
2972
2973         if (pf < grab) {
2974                 start = pf;
2975                 end = grab;
2976         } else {
2977                 end = pf;
2978                 start = grab;
2979         }
2980
2981         if (_drags->current_pointer_y() < grab_y()) {
2982                 y1 = _drags->current_pointer_y();
2983                 y2 = grab_y();
2984         } else {
2985                 y2 = _drags->current_pointer_y();
2986                 y1 = grab_y();
2987         }
2988
2989
2990         if (start != end || y1 != y2) {
2991
2992                 double x1 = _editor->frame_to_pixel (start);
2993                 double x2 = _editor->frame_to_pixel (end);
2994
2995                 _editor->rubberband_rect->property_x1() = x1;
2996                 _editor->rubberband_rect->property_y1() = y1;
2997                 _editor->rubberband_rect->property_x2() = x2;
2998                 _editor->rubberband_rect->property_y2() = y2;
2999
3000                 _editor->rubberband_rect->show();
3001                 _editor->rubberband_rect->raise_to_top();
3002
3003                 _editor->show_verbose_time_cursor (pf, 10);
3004         }
3005 }
3006
3007 void
3008 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3009 {
3010         if (movement_occurred) {
3011
3012                 motion (event, false);
3013
3014                 double y1,y2;
3015                 if (_drags->current_pointer_y() < grab_y()) {
3016                         y1 = _drags->current_pointer_y();
3017                         y2 = grab_y();
3018                 } else {
3019                         y2 = _drags->current_pointer_y();
3020                         y1 = grab_y();
3021                 }
3022
3023
3024                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3025                 bool committed;
3026
3027                 _editor->begin_reversible_command (_("rubberband selection"));
3028
3029                 if (grab_frame() < last_pointer_frame()) {
3030                         committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
3031                 } else {
3032                         committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
3033                 }
3034
3035                 if (!committed) {
3036                         _editor->commit_reversible_command ();
3037                 }
3038
3039         } else {
3040                 if (!getenv("ARDOUR_SAE")) {
3041                         _editor->selection->clear_tracks();
3042                 }
3043                 _editor->selection->clear_regions();
3044                 _editor->selection->clear_points ();
3045                 _editor->selection->clear_lines ();
3046         }
3047
3048         _editor->rubberband_rect->hide();
3049 }
3050
3051 void
3052 RubberbandSelectDrag::aborted ()
3053 {
3054         _editor->rubberband_rect->hide ();
3055 }
3056
3057 void
3058 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3059 {
3060         Drag::start_grab (event);
3061
3062         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3063 }
3064
3065 void
3066 TimeFXDrag::motion (GdkEvent* event, bool)
3067 {
3068         RegionView* rv = _primary;
3069
3070         nframes64_t const pf = adjusted_current_frame (event);
3071
3072         if (pf > rv->region()->position()) {
3073                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3074         }
3075
3076         _editor->show_verbose_time_cursor (pf, 10);
3077 }
3078
3079 void
3080 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3081 {
3082         _primary->get_time_axis_view().hide_timestretch ();
3083
3084         if (!movement_occurred) {
3085                 return;
3086         }
3087
3088         if (last_pointer_frame() < _primary->region()->position()) {
3089                 /* backwards drag of the left edge - not usable */
3090                 return;
3091         }
3092
3093         nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
3094
3095         float percentage = (double) newlen / (double) _primary->region()->length();
3096
3097 #ifndef USE_RUBBERBAND
3098         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3099         if (_primary->region()->data_type() == DataType::AUDIO) {
3100                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3101         }
3102 #endif
3103
3104         _editor->begin_reversible_command (_("timestretch"));
3105
3106         // XXX how do timeFX on multiple regions ?
3107
3108         RegionSelection rs;
3109         rs.add (_primary);
3110
3111         if (!_editor->time_stretch (rs, percentage) == 0) {
3112                 error << _("An error occurred while executing time stretch operation") << endmsg;
3113         }
3114 }
3115
3116 void
3117 TimeFXDrag::aborted ()
3118 {
3119         _primary->get_time_axis_view().hide_timestretch ();
3120 }
3121
3122
3123 void
3124 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3125 {
3126         Drag::start_grab (event);
3127 }
3128
3129 void
3130 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3131 {
3132         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3133 }
3134
3135 void
3136 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3137 {
3138         if (movement_occurred && _editor->session()) {
3139                 /* make sure we stop */
3140                 _editor->session()->request_transport_speed (0.0);
3141         }
3142 }
3143
3144 void
3145 ScrubDrag::aborted ()
3146 {
3147         /* XXX: TODO */
3148 }
3149
3150 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3151         : Drag (e, i)
3152         , _operation (o)
3153         , _copy (false)
3154         , _original_pointer_time_axis (-1)
3155         , _last_pointer_time_axis (-1)
3156 {
3157
3158 }
3159
3160 void
3161 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3162 {
3163         nframes64_t start = 0;
3164         nframes64_t end = 0;
3165
3166         if (_editor->session() == 0) {
3167                 return;
3168         }
3169
3170         Gdk::Cursor* cursor = 0;
3171
3172         switch (_operation) {
3173         case CreateSelection:
3174                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3175                         _copy = true;
3176                 } else {
3177                         _copy = false;
3178                 }
3179                 cursor = _editor->selector_cursor;
3180                 Drag::start_grab (event, cursor);
3181                 break;
3182
3183         case SelectionStartTrim:
3184                 if (_editor->clicked_axisview) {
3185                         _editor->clicked_axisview->order_selection_trims (_item, true);
3186                 }
3187                 Drag::start_grab (event, _editor->trimmer_cursor);
3188                 start = _editor->selection->time[_editor->clicked_selection].start;
3189                 _pointer_frame_offset = grab_frame() - start;
3190                 break;
3191
3192         case SelectionEndTrim:
3193                 if (_editor->clicked_axisview) {
3194                         _editor->clicked_axisview->order_selection_trims (_item, false);
3195                 }
3196                 Drag::start_grab (event, _editor->trimmer_cursor);
3197                 end = _editor->selection->time[_editor->clicked_selection].end;
3198                 _pointer_frame_offset = grab_frame() - end;
3199                 break;
3200
3201         case SelectionMove:
3202                 start = _editor->selection->time[_editor->clicked_selection].start;
3203                 Drag::start_grab (event, cursor);
3204                 _pointer_frame_offset = grab_frame() - start;
3205                 break;
3206         }
3207
3208         if (_operation == SelectionMove) {
3209                 _editor->show_verbose_time_cursor (start, 10);
3210         } else {
3211                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3212         }
3213
3214         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3215 }
3216
3217 void
3218 SelectionDrag::motion (GdkEvent* event, bool first_move)
3219 {
3220         nframes64_t start = 0;
3221         nframes64_t end = 0;
3222         nframes64_t length;
3223
3224         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3225         if (pending_time_axis.first == 0) {
3226                 return;
3227         }
3228         
3229         nframes64_t const pending_position = adjusted_current_frame (event);
3230
3231         /* only alter selection if things have changed */
3232
3233         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3234                 return;
3235         }
3236
3237         switch (_operation) {
3238         case CreateSelection:
3239         {
3240                 nframes64_t grab = grab_frame ();
3241
3242                 if (first_move) {
3243                         _editor->snap_to (grab);
3244                 }
3245
3246                 if (pending_position < grab_frame()) {
3247                         start = pending_position;
3248                         end = grab;
3249                 } else {
3250                         end = pending_position;
3251                         start = grab;
3252                 }
3253
3254                 /* first drag: Either add to the selection
3255                    or create a new selection
3256                 */
3257
3258                 if (first_move) {
3259
3260                         if (_copy) {
3261                                 /* adding to the selection */
3262                                 _editor->selection->add (_editor->clicked_axisview);
3263                                 _editor->clicked_selection = _editor->selection->add (start, end);
3264                                 _copy = false;
3265                         } else {
3266                                 /* new selection */
3267
3268                                 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3269                                         _editor->selection->set (_editor->clicked_axisview);
3270                                 }
3271                                 
3272                                 _editor->clicked_selection = _editor->selection->set (start, end);
3273                         }
3274                 }
3275
3276                 /* select the track that we're in */
3277                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3278                         _editor->selection->add (pending_time_axis.first);
3279                         _added_time_axes.push_back (pending_time_axis.first);
3280                 }
3281
3282                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3283                    tracks that we selected in the first place.
3284                 */
3285                 
3286                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3287                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3288
3289                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3290                 while (i != _added_time_axes.end()) {
3291
3292                         list<TimeAxisView*>::iterator tmp = i;
3293                         ++tmp;
3294                         
3295                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3296                                 _editor->selection->remove (*i);
3297                                 _added_time_axes.remove (*i);
3298                         }
3299
3300                         i = tmp;
3301                 }
3302
3303         }
3304         break;
3305
3306         case SelectionStartTrim:
3307
3308                 start = _editor->selection->time[_editor->clicked_selection].start;
3309                 end = _editor->selection->time[_editor->clicked_selection].end;
3310
3311                 if (pending_position > end) {
3312                         start = end;
3313                 } else {
3314                         start = pending_position;
3315                 }
3316                 break;
3317
3318         case SelectionEndTrim:
3319
3320                 start = _editor->selection->time[_editor->clicked_selection].start;
3321                 end = _editor->selection->time[_editor->clicked_selection].end;
3322
3323                 if (pending_position < start) {
3324                         end = start;
3325                 } else {
3326                         end = pending_position;
3327                 }
3328
3329                 break;
3330
3331         case SelectionMove:
3332
3333                 start = _editor->selection->time[_editor->clicked_selection].start;
3334                 end = _editor->selection->time[_editor->clicked_selection].end;
3335
3336                 length = end - start;
3337
3338                 start = pending_position;
3339                 _editor->snap_to (start);
3340
3341                 end = start + length;
3342
3343                 break;
3344         }
3345
3346         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3347                 _editor->start_canvas_autoscroll (1, 0);
3348         }
3349
3350         if (start != end) {
3351                 _editor->selection->replace (_editor->clicked_selection, start, end);
3352         }
3353
3354         if (_operation == SelectionMove) {
3355                 _editor->show_verbose_time_cursor(start, 10);
3356         } else {
3357                 _editor->show_verbose_time_cursor(pending_position, 10);
3358         }
3359 }
3360
3361 void
3362 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3363 {
3364         Session* s = _editor->session();
3365
3366         if (movement_occurred) {
3367                 motion (event, false);
3368                 /* XXX this is not object-oriented programming at all. ick */
3369                 if (_editor->selection->time.consolidate()) {
3370                         _editor->selection->TimeChanged ();
3371                 }
3372
3373                 /* XXX what if its a music time selection? */
3374                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3375                         s->request_play_range (&_editor->selection->time, true);
3376                 }
3377
3378
3379         } else {
3380                 /* just a click, no pointer movement.*/
3381
3382                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3383                         _editor->selection->clear_time();
3384                 }
3385
3386                 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3387                         _editor->selection->set (_editor->clicked_axisview);
3388                 }
3389                 
3390                 if (s && s->get_play_range () && s->transport_rolling()) {
3391                         s->request_stop (false, false);
3392                 }
3393
3394         }
3395
3396         _editor->stop_canvas_autoscroll ();
3397 }
3398
3399 void
3400 SelectionDrag::aborted ()
3401 {
3402         /* XXX: TODO */
3403 }
3404
3405 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3406         : Drag (e, i),
3407           _operation (o),
3408           _copy (false)
3409 {
3410         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3411         _drag_rect->hide ();
3412
3413         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3414         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3415 }
3416
3417 void
3418 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3419 {
3420         if (_editor->session() == 0) {
3421                 return;
3422         }
3423
3424         Gdk::Cursor* cursor = 0;
3425
3426         if (!_editor->temp_location) {
3427                 _editor->temp_location = new Location;
3428         }
3429
3430         switch (_operation) {
3431         case CreateRangeMarker:
3432         case CreateTransportMarker:
3433         case CreateCDMarker:
3434
3435                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3436                         _copy = true;
3437                 } else {
3438                         _copy = false;
3439                 }
3440                 cursor = _editor->selector_cursor;
3441                 break;
3442         }
3443
3444         Drag::start_grab (event, cursor);
3445
3446         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3447 }
3448
3449 void
3450 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3451 {
3452         nframes64_t start = 0;
3453         nframes64_t end = 0;
3454         ArdourCanvas::SimpleRect *crect;
3455
3456         switch (_operation) {
3457         case CreateRangeMarker:
3458                 crect = _editor->range_bar_drag_rect;
3459                 break;
3460         case CreateTransportMarker:
3461                 crect = _editor->transport_bar_drag_rect;
3462                 break;
3463         case CreateCDMarker:
3464                 crect = _editor->cd_marker_bar_drag_rect;
3465                 break;
3466         default:
3467                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3468                 return;
3469                 break;
3470         }
3471
3472         nframes64_t const pf = adjusted_current_frame (event);
3473
3474         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3475                 nframes64_t grab = grab_frame ();
3476                 _editor->snap_to (grab);
3477                 
3478                 if (pf < grab_frame()) {
3479                         start = pf;
3480                         end = grab;
3481                 } else {
3482                         end = pf;
3483                         start = grab;
3484                 }
3485
3486                 /* first drag: Either add to the selection
3487                    or create a new selection.
3488                 */
3489
3490                 if (first_move) {
3491
3492                         _editor->temp_location->set (start, end);
3493
3494                         crect->show ();
3495
3496                         update_item (_editor->temp_location);
3497                         _drag_rect->show();
3498                         //_drag_rect->raise_to_top();
3499
3500                 }
3501         }
3502
3503         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3504                 _editor->start_canvas_autoscroll (1, 0);
3505         }
3506
3507         if (start != end) {
3508                 _editor->temp_location->set (start, end);
3509
3510                 double x1 = _editor->frame_to_pixel (start);
3511                 double x2 = _editor->frame_to_pixel (end);
3512                 crect->property_x1() = x1;
3513                 crect->property_x2() = x2;
3514
3515                 update_item (_editor->temp_location);
3516         }
3517
3518         _editor->show_verbose_time_cursor (pf, 10);
3519
3520 }
3521
3522 void
3523 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3524 {
3525         Location * newloc = 0;
3526         string rangename;
3527         int flags;
3528
3529         if (movement_occurred) {
3530                 motion (event, false);
3531                 _drag_rect->hide();
3532
3533                 switch (_operation) {
3534                 case CreateRangeMarker:
3535                 case CreateCDMarker:
3536                     {
3537                         _editor->begin_reversible_command (_("new range marker"));
3538                         XMLNode &before = _editor->session()->locations()->get_state();
3539                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3540                         if (_operation == CreateCDMarker) {
3541                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3542                                 _editor->cd_marker_bar_drag_rect->hide();
3543                         }
3544                         else {
3545                                 flags = Location::IsRangeMarker;
3546                                 _editor->range_bar_drag_rect->hide();
3547                         }
3548                         newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3549                         _editor->session()->locations()->add (newloc, true);
3550                         XMLNode &after = _editor->session()->locations()->get_state();
3551                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3552                         _editor->commit_reversible_command ();
3553                         break;
3554                     }
3555
3556                 case CreateTransportMarker:
3557                         // popup menu to pick loop or punch
3558                         _editor->new_transport_marker_context_menu (&event->button, _item);
3559                         break;
3560                 }
3561         } else {
3562                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3563
3564                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3565
3566                         nframes64_t start;
3567                         nframes64_t end;
3568
3569                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3570
3571                         if (end == max_frames) {
3572                                 end = _editor->session()->current_end_frame ();
3573                         }
3574
3575                         if (start == max_frames) {
3576                                 start = _editor->session()->current_start_frame ();
3577                         }
3578
3579                         switch (_editor->mouse_mode) {
3580                         case MouseObject:
3581                                 /* find the two markers on either side and then make the selection from it */
3582                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3583                                 break;
3584
3585                         case MouseRange:
3586                                 /* find the two markers on either side of the click and make the range out of it */
3587                                 _editor->selection->set (start, end);
3588                                 break;
3589
3590                         default:
3591                                 break;
3592                         }
3593                 }
3594         }
3595
3596         _editor->stop_canvas_autoscroll ();
3597 }
3598
3599 void
3600 RangeMarkerBarDrag::aborted ()
3601 {
3602         /* XXX: TODO */
3603 }
3604
3605 void
3606 RangeMarkerBarDrag::update_item (Location* location)
3607 {
3608         double const x1 = _editor->frame_to_pixel (location->start());
3609         double const x2 = _editor->frame_to_pixel (location->end());
3610
3611         _drag_rect->property_x1() = x1;
3612         _drag_rect->property_x2() = x2;
3613 }
3614
3615 void
3616 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3617 {
3618         Drag::start_grab (event, _editor->zoom_cursor);
3619         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3620 }
3621
3622 void
3623 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3624 {
3625         nframes64_t start;
3626         nframes64_t end;
3627
3628         nframes64_t const pf = adjusted_current_frame (event);
3629
3630         nframes64_t grab = grab_frame ();
3631         _editor->snap_to_with_modifier (grab, event);
3632
3633         /* base start and end on initial click position */
3634         if (pf < grab) {
3635                 start = pf;
3636                 end = grab;
3637         } else {
3638                 end = pf;
3639                 start = grab;
3640         }
3641
3642         if (start != end) {
3643
3644                 if (first_move) {
3645                         _editor->zoom_rect->show();
3646                         _editor->zoom_rect->raise_to_top();
3647                 }
3648
3649                 _editor->reposition_zoom_rect(start, end);
3650
3651                 _editor->show_verbose_time_cursor (pf, 10);
3652         }
3653 }
3654
3655 void
3656 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3657 {
3658         if (movement_occurred) {
3659                 motion (event, false);
3660
3661                 if (grab_frame() < last_pointer_frame()) {
3662                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3663                 } else {
3664                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3665                 }
3666         } else {
3667                 _editor->temporal_zoom_to_frame (false, grab_frame());
3668                 /*
3669                 temporal_zoom_step (false);
3670                 center_screen (grab_frame());
3671                 */
3672         }
3673
3674         _editor->zoom_rect->hide();
3675 }
3676
3677 void
3678 MouseZoomDrag::aborted ()
3679 {
3680         _editor->zoom_rect->hide ();
3681 }
3682
3683 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3684         : Drag (e, i)
3685 {
3686         CanvasNoteEvent*     cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3687         region = &cnote->region_view();
3688 }
3689
3690 void
3691 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3692 {
3693         Drag::start_grab (event);
3694
3695         drag_delta_x = 0;
3696         drag_delta_note = 0;
3697
3698         double event_x;
3699         double event_y;
3700
3701         event_x = _drags->current_pointer_x();
3702         event_y = _drags->current_pointer_y();
3703
3704         _item->property_parent().get_value()->w2i(event_x, event_y);
3705
3706         last_x = region->snap_to_pixel(event_x);
3707         last_y = event_y;
3708
3709         CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3710
3711         if (!(was_selected = cnote->selected())) {
3712
3713                 /* tertiary-click means extend selection - we'll do that on button release,
3714                    so don't add it here, because otherwise we make it hard to figure
3715                    out the "extend-to" range.
3716                 */
3717
3718                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3719
3720                 if (!extend) {
3721                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3722
3723                         if (add) {
3724                                 region->note_selected (cnote, true);
3725                         } else {
3726                                 region->unique_select (cnote);
3727                         }
3728                 }
3729         }
3730 }
3731
3732 void
3733 NoteDrag::motion (GdkEvent*, bool)
3734 {
3735         MidiStreamView* streamview = region->midi_stream_view();
3736         double event_x;
3737         double event_y;
3738
3739         event_x = _drags->current_pointer_x();
3740         event_y = _drags->current_pointer_y();
3741
3742         _item->property_parent().get_value()->w2i(event_x, event_y);
3743
3744         event_x = region->snap_to_pixel(event_x);
3745
3746         double dx     = event_x - last_x;
3747         double dy     = event_y - last_y;
3748         last_x = event_x;
3749
3750         drag_delta_x += dx;
3751
3752         // Snap to note rows
3753
3754         if (abs (dy) < streamview->note_height()) {
3755                 dy = 0.0;
3756         } else {
3757                 int8_t this_delta_note;
3758                 if (dy > 0) {
3759                         this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3760                 } else {
3761                         this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3762                 }
3763                 drag_delta_note -= this_delta_note;
3764                 dy = streamview->note_height() * this_delta_note;
3765                 last_y = last_y + dy;
3766         }
3767
3768         if (dx || dy) {
3769                 region->move_selection (dx, dy);
3770
3771                 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3772                 char buf[4];
3773                 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3774                 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3775                 _editor->show_verbose_canvas_cursor_with (buf);
3776         }
3777 }
3778
3779 void
3780 NoteDrag::finished (GdkEvent* ev, bool moved)
3781 {
3782         ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3783
3784         if (!moved) {
3785                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3786
3787                         if (was_selected) {
3788                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3789                                 if (add) {
3790                                         region->note_deselected (cnote);
3791                                 }
3792                         } else {
3793                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3794                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3795
3796                                 if (!extend && !add && region->selection_size() > 1) {
3797                                         region->unique_select(cnote);
3798                                 } else if (extend) {
3799                                         region->note_selected (cnote, true, true);
3800                                 } else {
3801                                         /* it was added during button press */
3802                                 }
3803                         }
3804                 }
3805         } else {
3806                 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3807         }
3808 }
3809
3810 void
3811 NoteDrag::aborted ()
3812 {
3813         /* XXX: TODO */
3814 }
3815
3816 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3817         : Drag (e, i)
3818         , _ranges (r)
3819         , _nothing_to_drag (false)
3820 {
3821         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3822         assert (_atav);
3823
3824         _line = _atav->line ();
3825 }
3826
3827 void
3828 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3829 {
3830         Drag::start_grab (event, cursor);
3831
3832         list<ControlPoint*> points;
3833
3834         XMLNode* state = &_line->get_state ();
3835         
3836         if (_ranges.empty()) {
3837                 
3838                 uint32_t const N = _line->npoints ();
3839                 for (uint32_t i = 0; i < N; ++i) {
3840                         points.push_back (_line->nth (i));
3841                 }
3842                 
3843         } else {
3844
3845                 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3846                 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3847
3848                         /* fade into and out of the region that we're dragging;
3849                            64 samples length plucked out of thin air.
3850                         */
3851                         nframes64_t const h = (j->start + j->end) / 2;
3852                         nframes64_t a = j->start + 64;
3853                         if (a > h) {
3854                                 a = h;
3855                         }
3856                         nframes64_t b = j->end - 64;
3857                         if (b < h) {
3858                                 b = h;
3859                         }
3860                         
3861                         the_list->add (j->start, the_list->eval (j->start));
3862                         _line->add_always_in_view (j->start);
3863                         the_list->add (a, the_list->eval (a));
3864                         _line->add_always_in_view (a);
3865                         the_list->add (b, the_list->eval (b));
3866                         _line->add_always_in_view (b);
3867                         the_list->add (j->end, the_list->eval (j->end));
3868                         _line->add_always_in_view (j->end);
3869                 }
3870
3871                 uint32_t const N = _line->npoints ();
3872                 for (uint32_t i = 0; i < N; ++i) {
3873
3874                         ControlPoint* p = _line->nth (i);
3875
3876                         list<AudioRange>::const_iterator j = _ranges.begin ();
3877                         while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3878                                 ++j;
3879                         }
3880
3881                         if (j != _ranges.end()) {
3882                                 points.push_back (p);
3883                         }
3884                 }
3885         }
3886
3887         if (points.empty()) {
3888                 _nothing_to_drag = true;
3889                 return;
3890         }
3891
3892         _line->start_drag_multiple (points, 1 - (_drags->current_pointer_y() / _line->height ()), state);
3893 }
3894
3895 void
3896 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3897 {
3898         if (_nothing_to_drag) {
3899                 return;
3900         }
3901         
3902         float const f = 1 - (_drags->current_pointer_y() / _line->height());
3903
3904         /* we are ignoring x position for this drag, so we can just pass in anything */
3905         _line->drag_motion (0, f, true, false);
3906 }
3907
3908 void
3909 AutomationRangeDrag::finished (GdkEvent* event, bool)
3910 {
3911         if (_nothing_to_drag) {
3912                 return;
3913         }
3914         
3915         motion (event, false);
3916         _line->end_drag ();
3917         _line->clear_always_in_view ();
3918 }
3919
3920 void
3921 AutomationRangeDrag::aborted ()
3922 {
3923         _line->clear_always_in_view ();
3924         _line->reset ();
3925 }