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