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