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