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