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