86fbe134bb141571c934f57c668318f56eb88174
[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                         rv->get_time_axis_view().reveal_dependent_views (*rv);
878
879                         /* just change the model */
880
881                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
882
883                         if (dest_rtv->view()->layer_display() == Stacked) {
884                                 rv->region()->set_layer (dest_layer);
885                                 rv->region()->set_pending_explicit_relayer (true);
886                         }
887
888                         insert_result = modified_playlists.insert (playlist);
889
890                         if (insert_result.second) {
891                                 _editor->session()->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
892                         }
893                         /* freeze to avoid lots of relayering in the case of a multi-region drag */
894                         frozen_insert_result = frozen_playlists.insert(playlist);
895
896                         if (frozen_insert_result.second) {
897                                 playlist->freeze();
898                         }
899
900                         rv->region()->set_position (where, (void*) this);
901                 }
902
903                 if (changed_tracks && !_copy) {
904
905                         /* get the playlist where this drag started. we can't use rv->region()->playlist()
906                            because we may have copied the region and it has not been attached to a playlist.
907                         */
908
909                         source_tv = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
910                         ds = source_tv->get_diskstream();
911                         from_playlist = ds->playlist();
912
913                         assert (source_tv);
914                         assert (ds);
915                         assert (from_playlist);
916
917                         /* moved to a different audio track, without copying */
918
919                         /* the region that used to be in the old playlist is not
920                            moved to the new one - we use a copy of it. as a result,
921                            any existing editor for the region should no longer be
922                            visible.
923                         */
924
925                         rv->hide_region_editor();
926                         rv->fake_set_opaque (false);
927
928                         /* remove the region from the old playlist */
929
930                         insert_result = modified_playlists.insert (from_playlist);
931
932                         if (insert_result.second) {
933                                 _editor->session()->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
934                         }
935
936                         from_playlist->remove_region (rv->region());
937
938                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
939                            was selected in all of them, then removing it from a playlist will have removed all
940                            trace of it from the selection (i.e. there were N regions selected, we removed 1,
941                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
942                            corresponding regionview, and the selection is now empty).
943
944                            this could have invalidated any and all iterators into the region selection.
945
946                            the heuristic we use here is: if the region selection is empty, break out of the loop
947                            here. if the region selection is not empty, then restart the loop because we know that
948                            we must have removed at least the region(view) we've just been working on as well as any
949                            that we processed on previous iterations.
950
951                            EXCEPT .... if we are doing a copy drag, then the selection hasn't been modified and
952                            we can just iterate.
953                         */
954
955                         if (_views.empty()) {
956                                 break;
957                         } else {
958                                 i = _views.begin();
959                         }
960
961                 } else {
962                         ++i;
963                 }
964
965                 if (_copy) {
966                         copies.push_back (rv);
967                 }
968         }
969         /*
970            if we've created new regions either by copying or moving 
971            to a new track, we want to replace the old selection with the new ones 
972         */
973         if (new_views.size() > 0) {
974                 _editor->selection->set (new_views);
975         }
976
977         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
978                 (*p)->thaw();
979         }
980
981   out:
982         for (set<boost::shared_ptr<Playlist> >::iterator p = modified_playlists.begin(); p != modified_playlists.end(); ++p) {
983                 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p), 0, &(*p)->get_state()));
984         }
985
986         _editor->commit_reversible_command ();
987
988         for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
989                 delete *x;
990         }
991 }
992
993
994 bool
995 RegionMotionDrag::x_move_allowed () const
996 {
997         if (Config->get_edit_mode() == Lock) {
998                 /* in locked edit mode, reverse the usual meaning of _x_constrained */
999                 return _x_constrained;
1000         }
1001
1002         return !_x_constrained;
1003 }
1004
1005 void
1006 RegionMotionDrag::copy_regions (GdkEvent* event)
1007 {
1008         /* duplicate the regionview(s) and region(s) */
1009
1010         list<RegionView*> new_regionviews;
1011
1012         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1013
1014                 RegionView* rv = (*i);
1015                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1016                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
1017
1018                 const boost::shared_ptr<const Region> original = rv->region();
1019                 boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
1020
1021                 RegionView* nrv;
1022                 if (arv) {
1023                         boost::shared_ptr<AudioRegion> audioregion_copy
1024                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
1025                         nrv = new AudioRegionView (*arv, audioregion_copy);
1026                 } else if (mrv) {
1027                         boost::shared_ptr<MidiRegion> midiregion_copy
1028                                 = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
1029                         nrv = new MidiRegionView (*mrv, midiregion_copy);
1030                 } else {
1031                         continue;
1032                 }
1033
1034                 nrv->get_canvas_group()->show ();
1035                 new_regionviews.push_back (nrv);
1036
1037                 /* swap _primary to the copy */
1038
1039                 if (rv == _primary) {
1040                         _primary = nrv;
1041                 }
1042
1043                 /* ..and deselect the one we copied */
1044
1045                 rv->set_selected (false);
1046         }
1047
1048         if (new_regionviews.empty()) {
1049                 return;
1050         }
1051
1052         /* reflect the fact that we are dragging the copies */
1053
1054         _views = new_regionviews;
1055
1056         swap_grab (new_regionviews.front()->get_canvas_group (), 0, event ? event->motion.time : 0);
1057
1058         /*
1059            sync the canvas to what we think is its current state
1060            without it, the canvas seems to
1061            "forget" to update properly after the upcoming reparent()
1062            ..only if the mouse is in rapid motion at the time of the grab.
1063            something to do with regionview creation raking so long?
1064         */
1065         _editor->update_canvas_now();
1066 }
1067
1068 bool
1069 RegionMotionDrag::check_possible (RouteTimeAxisView** tv, layer_t* layer)
1070 {
1071         /* Which trackview is this ? */
1072
1073         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1074         (*tv) = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1075         (*layer) = tvp.second;
1076
1077         if (*tv && (*tv)->layer_display() == Overlaid) {
1078                 *layer = 0;
1079         }
1080
1081         /* The region motion is only processed if the pointer is over
1082            an audio track.
1083         */
1084
1085         if (!(*tv) || !(*tv)->is_track()) {
1086                 /* To make sure we hide the verbose canvas cursor when the mouse is
1087                    not held over and audiotrack.
1088                 */
1089                 _editor->hide_verbose_canvas_cursor ();
1090                 return false;
1091         }
1092
1093         return true;
1094 }
1095
1096 /** @param new_order New track order.
1097  *  @param old_order Old track order.
1098  *  @param visible_y_low Lowest visible order.
1099  *  @return true if y movement should not happen, otherwise false.
1100  */
1101 bool
1102 RegionMotionDrag::y_movement_disallowed (int new_order, int old_order, int y_span, TimeAxisViewSummary const & tavs) const
1103 {
1104         if (new_order != old_order) {
1105
1106                 /* this isn't the pointer track */
1107
1108                 if (y_span > 0) {
1109
1110                         /* moving up the canvas */
1111                         if ( (new_order - y_span) >= tavs.visible_y_low) {
1112
1113                                 int32_t n = 0;
1114
1115                                 /* work out where we'll end up with this y span, taking hidden TimeAxisViews into account */
1116                                 int32_t visible_tracks = 0;
1117                                 while (visible_tracks < y_span ) {
1118                                         visible_tracks++;
1119                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1120                                                 /* passing through a hidden track */
1121                                                 n--;
1122                                         }
1123                                 }
1124
1125                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1126                                         /* moving to a non-track; disallow */
1127                                         return true;
1128                                 }
1129
1130
1131                         } else {
1132                                 /* moving beyond the lowest visible track; disallow */
1133                                 return true;
1134                         }
1135
1136                 } else if (y_span < 0) {
1137
1138                         /* moving down the canvas */
1139                         if ((new_order - y_span) <= tavs.visible_y_high) {
1140
1141                                 int32_t visible_tracks = 0;
1142                                 int32_t n = 0;
1143                                 while (visible_tracks > y_span ) {
1144                                         visible_tracks--;
1145
1146                                         while (tavs.height_list[new_order - (visible_tracks - n)] == 0) {
1147                                                 /* passing through a hidden track */
1148                                                 n++;
1149                                         }
1150                                 }
1151
1152                                 if (tavs.tracks[new_order - (y_span - n)] != 0x00) {
1153                                         /* moving to a non-track; disallow */
1154                                         return true;
1155                                 }
1156
1157
1158                         } else {
1159
1160                                 /* moving beyond the highest visible track; disallow */
1161                                 return true;
1162                         }
1163                 }
1164
1165         } else {
1166
1167                 /* this is the pointer's track */
1168
1169                 if ((new_order - y_span) > tavs.visible_y_high) {
1170                         /* we will overflow */
1171                         return true;
1172                 } else if ((new_order - y_span) < tavs.visible_y_low) {
1173                         /* we will overflow */
1174                         return true;
1175                 }
1176         }
1177
1178         return false;
1179 }
1180
1181
1182 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1183         : RegionMotionDrag (e, i, p, v, b),
1184           _copy (c)
1185 {
1186         TimeAxisView* const tv = &_primary->get_time_axis_view ();
1187
1188         _dest_trackview = tv;
1189         if (tv->layer_display() == Overlaid) {
1190                 _dest_layer = 0;
1191         } else {
1192                 _dest_layer = _primary->region()->layer ();
1193         }
1194
1195         double speed = 1;
1196         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1197         if (rtv && rtv->is_track()) {
1198                 speed = rtv->get_diskstream()->speed ();
1199         }
1200
1201         _last_frame_position = static_cast<nframes64_t> (_primary->region()->position() / speed);
1202 }
1203
1204 void
1205 RegionMoveDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1206 {
1207         RegionMotionDrag::start_grab (event, c);
1208
1209         _pointer_frame_offset = grab_frame() - _last_frame_position;
1210 }
1211
1212 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, nframes64_t pos)
1213         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1214 {
1215         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1216                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1217
1218         _primary = v->view()->create_region_view (r, false, false);
1219
1220         _primary->get_canvas_group()->show ();
1221         _primary->set_position (pos, 0);
1222         _views.push_back (_primary);
1223
1224         _last_frame_position = pos;
1225
1226         _item = _primary->get_canvas_group ();
1227         _dest_trackview = v;
1228         _dest_layer = _primary->region()->layer ();
1229 }
1230
1231 map<RegionView*, pair<RouteTimeAxisView*, int> >
1232 RegionMotionDrag::find_time_axis_views_and_layers ()
1233 {
1234         map<RegionView*, pair<RouteTimeAxisView*, int> > tav;
1235
1236         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1237
1238                 double ix1, ix2, iy1, iy2;
1239                 (*i)->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
1240                 (*i)->get_canvas_frame()->i2w (ix1, iy1);
1241                 iy1 += _editor->vertical_adjustment.get_value() - _editor->canvas_timebars_vsize;
1242
1243                 pair<TimeAxisView*, int> tv = _editor->trackview_by_y_position (iy1);
1244                 tav[*i] = make_pair (dynamic_cast<RouteTimeAxisView*> (tv.first), tv.second);
1245         }
1246
1247         return tav;
1248 }
1249
1250
1251 void
1252 RegionInsertDrag::finished (GdkEvent* /*event*/, bool /*movement_occurred*/)
1253 {
1254         _editor->update_canvas_now ();
1255
1256         map<RegionView*, pair<RouteTimeAxisView*, int> > final = find_time_axis_views_and_layers ();
1257
1258         RouteTimeAxisView* dest_rtv = final[_primary].first;
1259
1260         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1261         _primary->get_canvas_group()->property_y() = 0;
1262
1263         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1264
1265         _editor->begin_reversible_command (_("insert region"));
1266         XMLNode& before = playlist->get_state ();
1267         playlist->add_region (_primary->region (), _last_frame_position);
1268         _editor->session()->add_command (new MementoCommand<Playlist> (*playlist, &before, &playlist->get_state()));
1269         _editor->commit_reversible_command ();
1270
1271         delete _primary;
1272         _primary = 0;
1273         _views.clear ();
1274 }
1275
1276 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1277         : RegionMoveDrag (e, i, p, v, false, false)
1278 {
1279
1280 }
1281
1282 struct RegionSelectionByPosition {
1283     bool operator() (RegionView*a, RegionView* b) {
1284             return a->region()->position () < b->region()->position();
1285     }
1286 };
1287
1288 void
1289 RegionSpliceDrag::motion (GdkEvent* event, bool)
1290 {
1291         RouteTimeAxisView* tv;
1292         layer_t layer;
1293
1294         if (!check_possible (&tv, &layer)) {
1295                 return;
1296         }
1297
1298         int dir;
1299
1300         if ((current_pointer_x() - last_pointer_x()) > 0) {
1301                 dir = 1;
1302         } else {
1303                 dir = -1;
1304         }
1305
1306         RegionSelection copy (_editor->selection->regions);
1307
1308         RegionSelectionByPosition cmp;
1309         copy.sort (cmp);
1310
1311         nframes64_t const pf = adjusted_current_frame (event);
1312
1313         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1314
1315                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1316
1317                 if (!atv) {
1318                         continue;
1319                 }
1320
1321                 boost::shared_ptr<Playlist> playlist;
1322
1323                 if ((playlist = atv->playlist()) == 0) {
1324                         continue;
1325                 }
1326
1327                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1328                         continue;
1329                 }
1330
1331                 if (dir > 0) {
1332                         if (pf < (*i)->region()->last_frame() + 1) {
1333                                 continue;
1334                         }
1335                 } else {
1336                         if (pf > (*i)->region()->first_frame()) {
1337                                 continue;
1338                         }
1339                 }
1340
1341
1342                 playlist->shuffle ((*i)->region(), dir);
1343         }
1344 }
1345
1346 void
1347 RegionSpliceDrag::finished (GdkEvent* /*event*/, bool)
1348 {
1349
1350 }
1351
1352
1353 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1354         : Drag (e, i),
1355           _view (v)
1356 {
1357
1358 }
1359
1360 void
1361 RegionCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1362 {
1363         _dest_trackview = _view;
1364
1365         Drag::start_grab (event);
1366 }
1367
1368
1369 void
1370 RegionCreateDrag::motion (GdkEvent* /*event*/, bool first_move)
1371 {
1372         if (first_move) {
1373                 // TODO: create region-create-drag region view here
1374         }
1375
1376         // TODO: resize region-create-drag region view here
1377 }
1378
1379 void
1380 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
1381 {
1382         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (_dest_trackview);
1383
1384         if (!mtv) {
1385                 return;
1386         }
1387
1388         if (!movement_occurred) {
1389                 mtv->add_region (grab_frame ());
1390         } else {
1391                 motion (event, false);
1392                 // TODO: create region-create-drag region here
1393         }
1394 }
1395
1396 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1397         : Drag (e, i)
1398         , region (0)
1399 {
1400
1401 }
1402
1403 void
1404 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1405 {
1406         Gdk::Cursor     cursor;
1407         ArdourCanvas::CanvasNote*     cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
1408
1409         Drag::start_grab (event);
1410
1411         region = &cnote->region_view();
1412
1413         double region_start = region->get_position_pixels();
1414         double middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1415
1416         if (grab_x() <= middle_point) {
1417                 cursor = Gdk::Cursor(Gdk::LEFT_SIDE);
1418                 at_front = true;
1419         } else {
1420                 cursor = Gdk::Cursor(Gdk::RIGHT_SIDE);
1421                 at_front = false;
1422         }
1423
1424         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, cursor, event->motion.time);
1425
1426         if (event->motion.state & Keyboard::PrimaryModifier) {
1427                 relative = false;
1428         } else {
1429                 relative = true;
1430         }
1431
1432         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1433
1434         if (ms.size() > 1) {
1435                 /* has to be relative, may make no sense otherwise */
1436                 relative = true;
1437         }
1438
1439         region->note_selected (cnote, true);
1440
1441         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1442                 MidiRegionSelection::iterator next;
1443                 next = r;
1444                 ++next;
1445                 (*r)->begin_resizing (at_front);
1446                 r = next;
1447         }
1448 }
1449
1450 void
1451 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1452 {
1453         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1454         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1455                 (*r)->update_resizing (at_front, current_pointer_x() - grab_x(), relative);
1456         }
1457 }
1458
1459 void
1460 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1461 {
1462         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1463         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1464                 (*r)->commit_resizing (at_front, current_pointer_x() - grab_x(), relative);
1465         }
1466 }
1467
1468 void
1469 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1470 {
1471
1472 }
1473
1474 void
1475 RegionGainDrag::finished (GdkEvent *, bool)
1476 {
1477
1478 }
1479
1480 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1481         : RegionDrag (e, i, p, v)
1482 {
1483
1484 }
1485
1486 void
1487 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
1488 {
1489         double speed = 1.0;
1490         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1491         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1492
1493         if (tv && tv->is_track()) {
1494                 speed = tv->get_diskstream()->speed();
1495         }
1496
1497         nframes64_t region_start = (nframes64_t) (_primary->region()->position() / speed);
1498         nframes64_t region_end = (nframes64_t) (_primary->region()->last_frame() / speed);
1499         nframes64_t region_length = (nframes64_t) (_primary->region()->length() / speed);
1500
1501         Drag::start_grab (event, _editor->trimmer_cursor);
1502
1503         nframes64_t const pf = adjusted_current_frame (event);
1504
1505         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1506                 _operation = ContentsTrim;
1507         } else {
1508                 /* These will get overridden for a point trim.*/
1509                 if (pf < (region_start + region_length/2)) {
1510                         /* closer to start */
1511                         _operation = StartTrim;
1512                 } else if (pf > (region_end - region_length/2)) {
1513                         /* closer to end */
1514                         _operation = EndTrim;
1515                 }
1516         }
1517
1518         switch (_operation) {
1519         case StartTrim:
1520                 _editor->show_verbose_time_cursor (region_start, 10);
1521                 break;
1522         case EndTrim:
1523                 _editor->show_verbose_time_cursor (region_end, 10);
1524                 break;
1525         case ContentsTrim:
1526                 _editor->show_verbose_time_cursor (pf, 10);
1527                 break;
1528         }
1529 }
1530
1531 void
1532 TrimDrag::motion (GdkEvent* event, bool first_move)
1533 {
1534         RegionView* rv = _primary;
1535         nframes64_t frame_delta = 0;
1536
1537         bool left_direction;
1538         bool obey_snap = !Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier());
1539
1540         /* snap modifier works differently here..
1541            its current state has to be passed to the
1542            various trim functions in order to work properly
1543         */
1544
1545         double speed = 1.0;
1546         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1547         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1548         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1549
1550         if (tv && tv->is_track()) {
1551                 speed = tv->get_diskstream()->speed();
1552         }
1553
1554         nframes64_t const pf = adjusted_current_frame (event);
1555
1556         if (last_pointer_frame() > pf) {
1557                 left_direction = true;
1558         } else {
1559                 left_direction = false;
1560         }
1561
1562         if (first_move) {
1563
1564                 string trim_type;
1565
1566                 switch (_operation) {
1567                 case StartTrim:
1568                         trim_type = "Region start trim";
1569                         break;
1570                 case EndTrim:
1571                         trim_type = "Region end trim";
1572                         break;
1573                 case ContentsTrim:
1574                         trim_type = "Region content trim";
1575                         break;
1576                 }
1577
1578                 _editor->begin_reversible_command (trim_type);
1579                 _have_transaction = true;
1580
1581                 for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1582                         (*i)->fake_set_opaque(false);
1583                         (*i)->region()->freeze ();
1584
1585                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
1586
1587                         if (arv){
1588                                 arv->temporarily_hide_envelope ();
1589                         }
1590
1591                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
1592                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1593
1594                         if (insert_result.second) {
1595                                 _editor->session()->add_command(new MementoCommand<Playlist>(*pl, &pl->get_state(), 0));
1596                                 pl->freeze();
1597                         }
1598                 }
1599         }
1600
1601         if (pf == last_pointer_frame()) {
1602                 return;
1603         }
1604
1605         /* XXX i hope to god that we can really conclude this ... */
1606         _have_transaction = true;
1607
1608         if (left_direction) {
1609                 frame_delta = (last_pointer_frame() - pf);
1610         } else {
1611                 frame_delta = (pf - last_pointer_frame());
1612         }
1613
1614         bool non_overlap_trim = false;
1615
1616         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1617                 non_overlap_trim = true;
1618         }
1619
1620         switch (_operation) {
1621         case StartTrim:
1622                 if ((left_direction == false) && (pf <= rv->region()->first_frame()/speed)) {
1623                         break;
1624                 } else {
1625
1626                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1627                                 _editor->single_start_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1628                         }
1629                         break;
1630                 }
1631
1632         case EndTrim:
1633                 if ((left_direction == true) && (pf > (nframes64_t) (rv->region()->last_frame()/speed))) {
1634                         break;
1635                 } else {
1636
1637                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1638                                 _editor->single_end_trim (**i, frame_delta, left_direction, obey_snap, non_overlap_trim);
1639                         }
1640                         break;
1641                 }
1642
1643         case ContentsTrim:
1644                 {
1645                         bool swap_direction = false;
1646
1647                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1648                                 swap_direction = true;
1649                         }
1650
1651                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1652                                 _editor->single_contents_trim (**i, frame_delta, left_direction, swap_direction, obey_snap);
1653                         }
1654                 }
1655                 break;
1656         }
1657
1658         switch (_operation) {
1659         case StartTrim:
1660                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->position()/speed), 10);
1661                 break;
1662         case EndTrim:
1663                 _editor->show_verbose_time_cursor((nframes64_t) (rv->region()->last_frame()/speed), 10);
1664                 break;
1665         case ContentsTrim:
1666                 _editor->show_verbose_time_cursor (pf, 10);
1667                 break;
1668         }
1669 }
1670
1671
1672 void
1673 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1674 {
1675         if (movement_occurred) {
1676                 motion (event, false);
1677
1678                 if (!_editor->selection->selected (_primary)) {
1679                         _editor->thaw_region_after_trim (*_primary);
1680                 } else {
1681
1682                         for (list<RegionView*>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1683                                 _editor->thaw_region_after_trim (**i);
1684                                 (*i)->fake_set_opaque (true);
1685                         }
1686                 }
1687                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1688                         (*p)->thaw ();
1689                         if (_have_transaction) {
1690                                 _editor->session()->add_command (new MementoCommand<Playlist>(*(*p).get(), 0, &(*p)->get_state()));
1691                         }
1692                 }
1693
1694                 _editor->motion_frozen_playlists.clear ();
1695
1696                 if (_have_transaction) {
1697                         _editor->commit_reversible_command();
1698                 }
1699
1700         } else {
1701                 /* no mouse movement */
1702                 _editor->point_trim (event);
1703         }
1704 }
1705
1706 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1707         : Drag (e, i),
1708           _copy (c)
1709 {
1710         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1711         assert (_marker);
1712 }
1713
1714 void
1715 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1716 {
1717         if (_copy) {
1718                 // create a dummy marker for visual representation of moving the copy.
1719                 // The actual copying is not done before we reach the finish callback.
1720                 char name[64];
1721                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1722                 MeterMarker* new_marker = new MeterMarker(*_editor, *_editor->meter_group, ARDOUR_UI::config()->canvasvar_MeterMarker.get(), name,
1723                                                           *new MeterSection (_marker->meter()));
1724
1725                 _item = &new_marker->the_item ();
1726                 _marker = new_marker;
1727
1728         } else {
1729
1730                 MetricSection& section (_marker->meter());
1731
1732                 if (!section.movable()) {
1733                         return;
1734                 }
1735
1736         }
1737
1738         Drag::start_grab (event, cursor);
1739
1740         _pointer_frame_offset = grab_frame() - _marker->meter().frame();
1741
1742         _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1743 }
1744
1745 void
1746 MeterMarkerDrag::motion (GdkEvent* event, bool)
1747 {
1748         nframes64_t const pf = adjusted_current_frame (event);
1749
1750         if (pf == last_pointer_frame()) {
1751                 return;
1752         }
1753
1754         _marker->set_position (pf);
1755         
1756         _editor->show_verbose_time_cursor (pf, 10);
1757 }
1758
1759 void
1760 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1761 {
1762         if (!movement_occurred) {
1763                 return;
1764         }
1765
1766         motion (event, false);
1767
1768         BBT_Time when;
1769
1770         TempoMap& map (_editor->session()->tempo_map());
1771         map.bbt_time (last_pointer_frame(), when);
1772
1773         if (_copy == true) {
1774                 _editor->begin_reversible_command (_("copy meter mark"));
1775                 XMLNode &before = map.get_state();
1776                 map.add_meter (_marker->meter(), when);
1777                 XMLNode &after = map.get_state();
1778                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1779                 _editor->commit_reversible_command ();
1780
1781                 // delete the dummy marker we used for visual representation of copying.
1782                 // a new visual marker will show up automatically.
1783                 delete _marker;
1784         } else {
1785                 _editor->begin_reversible_command (_("move meter mark"));
1786                 XMLNode &before = map.get_state();
1787                 map.move_meter (_marker->meter(), when);
1788                 XMLNode &after = map.get_state();
1789                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1790                 _editor->commit_reversible_command ();
1791         }
1792 }
1793
1794 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1795         : Drag (e, i),
1796           _copy (c)
1797 {
1798         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1799         assert (_marker);
1800 }
1801
1802 void
1803 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1804 {
1805
1806         if (_copy) {
1807
1808                 // create a dummy marker for visual representation of moving the copy.
1809                 // The actual copying is not done before we reach the finish callback.
1810                 char name[64];
1811                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1812                 TempoMarker* new_marker = new TempoMarker(*_editor, *_editor->tempo_group, ARDOUR_UI::config()->canvasvar_TempoMarker.get(), name,
1813                                                           *new TempoSection (_marker->tempo()));
1814
1815                 _item = &new_marker->the_item ();
1816                 _marker = new_marker;
1817
1818         } else {
1819
1820                 MetricSection& section (_marker->tempo());
1821
1822                 if (!section.movable()) {
1823                         return;
1824                 }
1825         }
1826
1827         Drag::start_grab (event, cursor);
1828
1829         _pointer_frame_offset = grab_frame() - _marker->tempo().frame();
1830         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1831 }
1832
1833 void
1834 TempoMarkerDrag::motion (GdkEvent* event, bool)
1835 {
1836         nframes64_t const pf = adjusted_current_frame (event);
1837
1838         if (pf == last_pointer_frame()) {
1839                 return;
1840         }
1841
1842         /* OK, we've moved far enough to make it worth actually move the thing. */
1843
1844         _marker->set_position (pf);
1845
1846         _editor->show_verbose_time_cursor (pf, 10);
1847 }
1848
1849 void
1850 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1851 {
1852         if (!movement_occurred) {
1853                 return;
1854         }
1855
1856         motion (event, false);
1857
1858         BBT_Time when;
1859
1860         TempoMap& map (_editor->session()->tempo_map());
1861         map.bbt_time (last_pointer_frame(), when);
1862
1863         if (_copy == true) {
1864                 _editor->begin_reversible_command (_("copy tempo mark"));
1865                 XMLNode &before = map.get_state();
1866                 map.add_tempo (_marker->tempo(), when);
1867                 XMLNode &after = map.get_state();
1868                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1869                 _editor->commit_reversible_command ();
1870
1871                 // delete the dummy marker we used for visual representation of copying.
1872                 // a new visual marker will show up automatically.
1873                 delete _marker;
1874         } else {
1875                 _editor->begin_reversible_command (_("move tempo mark"));
1876                 XMLNode &before = map.get_state();
1877                 map.move_tempo (_marker->tempo(), when);
1878                 XMLNode &after = map.get_state();
1879                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1880                 _editor->commit_reversible_command ();
1881         }
1882 }
1883
1884
1885 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1886         : Drag (e, i),
1887           _stop (s)
1888 {
1889         _cursor = reinterpret_cast<EditorCursor*> (_item->get_data ("cursor"));
1890         assert (_cursor);
1891 }
1892
1893 void
1894 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
1895 {
1896         Drag::start_grab (event, c);
1897
1898         if (!_stop) {
1899
1900                 nframes64_t where = _editor->event_frame (event, 0, 0);
1901
1902                 _editor->snap_to_with_modifier (where, event);
1903                 _editor->playhead_cursor->set_position (where);
1904
1905         }
1906
1907         if (_cursor == _editor->playhead_cursor) {
1908                 _editor->_dragging_playhead = true;
1909
1910                 if (_editor->session() && _was_rolling && _stop) {
1911                         _editor->session()->request_stop ();
1912                 }
1913
1914                 if (_editor->session() && _editor->session()->is_auditioning()) {
1915                         _editor->session()->cancel_audition ();
1916                 }
1917         }
1918
1919         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1920 }
1921
1922 void
1923 CursorDrag::motion (GdkEvent* event, bool)
1924 {
1925         nframes64_t const adjusted_frame = adjusted_current_frame (event);
1926
1927         if (adjusted_frame == last_pointer_frame()) {
1928                 return;
1929         }
1930
1931         _cursor->set_position (adjusted_frame);
1932
1933         _editor->show_verbose_time_cursor (_cursor->current_frame, 10);
1934
1935 #ifdef GTKOSX
1936         _editor->update_canvas_now ();
1937 #endif
1938         _editor->UpdateAllTransportClocks (_cursor->current_frame);
1939 }
1940
1941 void
1942 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
1943 {
1944         _editor->_dragging_playhead = false;
1945
1946         if (!movement_occurred && _stop) {
1947                 return;
1948         }
1949
1950         motion (event, false);
1951
1952         if (_item == &_editor->playhead_cursor->canvas_item) {
1953                 if (_editor->session()) {
1954                         _editor->session()->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
1955                         _editor->_pending_locate_request = true;
1956                 }
1957         }
1958 }
1959
1960 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1961         : RegionDrag (e, i, p, v)
1962 {
1963
1964 }
1965
1966 void
1967 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1968 {
1969         Drag::start_grab (event, cursor);
1970
1971         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
1972         boost::shared_ptr<AudioRegion> const r = a->audio_region ();
1973
1974         _pointer_frame_offset = grab_frame() - ((nframes64_t) r->fade_in()->back()->when + r->position());
1975         _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
1976 }
1977
1978 void
1979 FadeInDrag::motion (GdkEvent* event, bool)
1980 {
1981         nframes64_t fade_length;
1982
1983         nframes64_t const pos = adjusted_current_frame (event);
1984
1985         boost::shared_ptr<Region> region = _primary->region ();
1986
1987         if (pos < (region->position() + 64)) {
1988                 fade_length = 64; // this should be a minimum defined somewhere
1989         } else if (pos > region->last_frame()) {
1990                 fade_length = region->length();
1991         } else {
1992                 fade_length = pos - region->position();
1993         }
1994
1995         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
1996
1997                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
1998
1999                 if (!tmp) {
2000                         continue;
2001                 }
2002
2003                 tmp->reset_fade_in_shape_width (fade_length);
2004         }
2005
2006         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2007 }
2008
2009 void
2010 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2011 {
2012         if (!movement_occurred) {
2013                 return;
2014         }
2015
2016         nframes64_t fade_length;
2017
2018         nframes64_t const pos = adjusted_current_frame (event);
2019
2020         boost::shared_ptr<Region> region = _primary->region ();
2021
2022         if (pos < (region->position() + 64)) {
2023                 fade_length = 64; // this should be a minimum defined somewhere
2024         } else if (pos > region->last_frame()) {
2025                 fade_length = region->length();
2026         } else {
2027                 fade_length = pos - region->position();
2028         }
2029
2030         _editor->begin_reversible_command (_("change fade in length"));
2031
2032         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2033
2034                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2035
2036                 if (!tmp) {
2037                         continue;
2038                 }
2039
2040                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2041                 XMLNode &before = alist->get_state();
2042
2043                 tmp->audio_region()->set_fade_in_length (fade_length);
2044                 tmp->audio_region()->set_fade_in_active (true);
2045
2046                 XMLNode &after = alist->get_state();
2047                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2048         }
2049
2050         _editor->commit_reversible_command ();
2051 }
2052
2053 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2054         : RegionDrag (e, i, p, v)
2055 {
2056
2057 }
2058
2059 void
2060 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2061 {
2062         Drag::start_grab (event, cursor);
2063
2064         AudioRegionView* a = dynamic_cast<AudioRegionView*> (_primary);
2065         boost::shared_ptr<AudioRegion> r = a->audio_region ();
2066
2067         _pointer_frame_offset = grab_frame() - (r->length() - (nframes64_t) r->fade_out()->back()->when + r->position());
2068         _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2069 }
2070
2071 void
2072 FadeOutDrag::motion (GdkEvent* event, bool)
2073 {
2074         nframes64_t fade_length;
2075
2076         nframes64_t const pos = adjusted_current_frame (event);
2077
2078         boost::shared_ptr<Region> region = _primary->region ();
2079
2080         if (pos > (region->last_frame() - 64)) {
2081                 fade_length = 64; // this should really be a minimum fade defined somewhere
2082         }
2083         else if (pos < region->position()) {
2084                 fade_length = region->length();
2085         }
2086         else {
2087                 fade_length = region->last_frame() - pos;
2088         }
2089
2090         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2091
2092                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2093
2094                 if (!tmp) {
2095                         continue;
2096                 }
2097
2098                 tmp->reset_fade_out_shape_width (fade_length);
2099         }
2100
2101         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2102 }
2103
2104 void
2105 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2106 {
2107         if (!movement_occurred) {
2108                 return;
2109         }
2110
2111         nframes64_t fade_length;
2112
2113         nframes64_t const pos = adjusted_current_frame (event);
2114
2115         boost::shared_ptr<Region> region = _primary->region ();
2116
2117         if (pos > (region->last_frame() - 64)) {
2118                 fade_length = 64; // this should really be a minimum fade defined somewhere
2119         }
2120         else if (pos < region->position()) {
2121                 fade_length = region->length();
2122         }
2123         else {
2124                 fade_length = region->last_frame() - pos;
2125         }
2126
2127         _editor->begin_reversible_command (_("change fade out length"));
2128
2129         for (RegionSelection::iterator i = _views.begin(); i != _views.end(); ++i) {
2130
2131                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*i);
2132
2133                 if (!tmp) {
2134                         continue;
2135                 }
2136
2137                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2138                 XMLNode &before = alist->get_state();
2139
2140                 tmp->audio_region()->set_fade_out_length (fade_length);
2141                 tmp->audio_region()->set_fade_out_active (true);
2142
2143                 XMLNode &after = alist->get_state();
2144                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2145         }
2146
2147         _editor->commit_reversible_command ();
2148 }
2149
2150 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2151         : Drag (e, i)
2152 {
2153         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2154         assert (_marker);
2155
2156         _points.push_back (Gnome::Art::Point (0, 0));
2157         _points.push_back (Gnome::Art::Point (0, _editor->physical_screen_height));
2158
2159         _line = new ArdourCanvas::Line (*_editor->timebar_group);
2160         _line->property_width_pixels() = 1;
2161         _line->property_points () = _points;
2162         _line->hide ();
2163
2164         _line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MarkerDragLine.get();
2165 }
2166
2167 MarkerDrag::~MarkerDrag ()
2168 {
2169         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2170                 delete *i;
2171         }
2172 }
2173
2174 void
2175 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2176 {
2177         Drag::start_grab (event, cursor);
2178
2179         bool is_start;
2180
2181         Location *location = _editor->find_location_from_marker (_marker, is_start);
2182         _editor->_dragging_edit_point = true;
2183
2184         _pointer_frame_offset = grab_frame() - (is_start ? location->start() : location->end());
2185
2186         update_item (location);
2187
2188         // _drag_line->show();
2189         // _line->raise_to_top();
2190
2191         if (is_start) {
2192                 _editor->show_verbose_time_cursor (location->start(), 10);
2193         } else {
2194                 _editor->show_verbose_time_cursor (location->end(), 10);
2195         }
2196
2197         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2198
2199         switch (op) {
2200         case Selection::Toggle:
2201                 _editor->selection->toggle (_marker);
2202                 break;
2203         case Selection::Set:
2204                 if (!_editor->selection->selected (_marker)) {
2205                         _editor->selection->set (_marker);
2206                 }
2207                 break;
2208         case Selection::Extend:
2209         {
2210                 Locations::LocationList ll;
2211                 list<Marker*> to_add;
2212                 nframes64_t s, e;
2213                 _editor->selection->markers.range (s, e);
2214                 s = min (_marker->position(), s);
2215                 e = max (_marker->position(), e);
2216                 s = min (s, e);
2217                 e = max (s, e);
2218                 if (e < max_frames) {
2219                         ++e;
2220                 }
2221                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2222                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2223                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2224                         if (lm) {
2225                                 if (lm->start) {
2226                                         to_add.push_back (lm->start);
2227                                 }
2228                                 if (lm->end) {
2229                                         to_add.push_back (lm->end);
2230                                 }
2231                         }
2232                 }
2233                 if (!to_add.empty()) {
2234                         _editor->selection->add (to_add);
2235                 }
2236                 break;
2237         }
2238         case Selection::Add:
2239                 _editor->selection->add (_marker);
2240                 break;
2241         }
2242
2243         /* set up copies for us to manipulate during the drag */
2244
2245         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2246                 Location *l = _editor->find_location_from_marker (*i, is_start);
2247                 _copied_locations.push_back (new Location (*l));
2248         }
2249 }
2250
2251 void
2252 MarkerDrag::motion (GdkEvent* event, bool)
2253 {
2254         nframes64_t f_delta = 0;
2255         bool is_start;
2256         bool move_both = false;
2257         Marker* marker;
2258         Location  *real_location;
2259         Location *copy_location = 0;
2260
2261         nframes64_t const newframe = adjusted_current_frame (event);
2262
2263         nframes64_t next = newframe;
2264
2265         if (newframe == last_pointer_frame()) {
2266                 return;
2267         }
2268
2269         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2270                 move_both = true;
2271         }
2272
2273         MarkerSelection::iterator i;
2274         list<Location*>::iterator x;
2275
2276         /* find the marker we're dragging, and compute the delta */
2277
2278         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2279              x != _copied_locations.end() && i != _editor->selection->markers.end();
2280              ++i, ++x) {
2281
2282                 copy_location = *x;
2283                 marker = *i;
2284
2285                 if (marker == _marker) {
2286
2287                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2288                                 /* que pasa ?? */
2289                                 return;
2290                         }
2291
2292                         if (real_location->is_mark()) {
2293                                 f_delta = newframe - copy_location->start();
2294                         } else {
2295
2296
2297                                 switch (marker->type()) {
2298                                 case Marker::Start:
2299                                 case Marker::LoopStart:
2300                                 case Marker::PunchIn:
2301                                         f_delta = newframe - copy_location->start();
2302                                         break;
2303
2304                                 case Marker::End:
2305                                 case Marker::LoopEnd:
2306                                 case Marker::PunchOut:
2307                                         f_delta = newframe - copy_location->end();
2308                                         break;
2309                                 default:
2310                                         /* what kind of marker is this ? */
2311                                         return;
2312                                 }
2313                         }
2314                         break;
2315                 }
2316         }
2317
2318         if (i == _editor->selection->markers.end()) {
2319                 /* hmm, impossible - we didn't find the dragged marker */
2320                 return;
2321         }
2322
2323         /* now move them all */
2324
2325         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2326              x != _copied_locations.end() && i != _editor->selection->markers.end();
2327              ++i, ++x) {
2328
2329                 copy_location = *x;
2330                 marker = *i;
2331
2332                 /* call this to find out if its the start or end */
2333
2334                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2335                         continue;
2336                 }
2337
2338                 if (real_location->locked()) {
2339                         continue;
2340                 }
2341
2342                 if (copy_location->is_mark()) {
2343
2344                         /* just move it */
2345
2346                         copy_location->set_start (copy_location->start() + f_delta);
2347
2348                 } else {
2349
2350                         nframes64_t new_start = copy_location->start() + f_delta;
2351                         nframes64_t new_end = copy_location->end() + f_delta;
2352
2353                         if (is_start) { // start-of-range marker
2354
2355                                 if (move_both) {
2356                                         copy_location->set_start (new_start);
2357                                         copy_location->set_end (new_end);
2358                                 } else  if (new_start < copy_location->end()) {
2359                                         copy_location->set_start (new_start);
2360                                 } else {
2361                                         _editor->snap_to (next, 1, true);
2362                                         copy_location->set_end (next);
2363                                         copy_location->set_start (newframe);
2364                                 }
2365
2366                         } else { // end marker
2367
2368                                 if (move_both) {
2369                                         copy_location->set_end (new_end);
2370                                         copy_location->set_start (new_start);
2371                                 } else if (new_end > copy_location->start()) {
2372                                         copy_location->set_end (new_end);
2373                                 } else if (newframe > 0) {
2374                                         _editor->snap_to (next, -1, true);
2375                                         copy_location->set_start (next);
2376                                         copy_location->set_end (newframe);
2377                                 }
2378                         }
2379                 }
2380
2381                 update_item (copy_location);
2382
2383                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2384
2385                 if (lm) {
2386                         lm->set_position (copy_location->start(), copy_location->end());
2387                 }
2388         }
2389
2390         assert (!_copied_locations.empty());
2391
2392         _editor->show_verbose_time_cursor (newframe, 10);
2393
2394 #ifdef GTKOSX
2395         _editor->update_canvas_now ();
2396 #endif
2397 }
2398
2399 void
2400 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2401 {
2402         if (!movement_occurred) {
2403
2404                 /* just a click, do nothing but finish
2405                    off the selection process
2406                 */
2407
2408                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2409
2410                 switch (op) {
2411                 case Selection::Set:
2412                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2413                                 _editor->selection->set (_marker);
2414                         }
2415                         break;
2416
2417                 case Selection::Toggle:
2418                 case Selection::Extend:
2419                 case Selection::Add:
2420                         break;
2421                 }
2422
2423                 return;
2424         }
2425
2426         _editor->_dragging_edit_point = false;
2427
2428         _editor->begin_reversible_command ( _("move marker") );
2429         XMLNode &before = _editor->session()->locations()->get_state();
2430
2431         MarkerSelection::iterator i;
2432         list<Location*>::iterator x;
2433         bool is_start;
2434
2435         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2436              x != _copied_locations.end() && i != _editor->selection->markers.end();
2437              ++i, ++x) {
2438
2439                 Location * location = _editor->find_location_from_marker (*i, is_start);
2440
2441                 if (location) {
2442
2443                         if (location->locked()) {
2444                                 return;
2445                         }
2446
2447                         if (location->is_mark()) {
2448                                 location->set_start ((*x)->start());
2449                         } else {
2450                                 location->set ((*x)->start(), (*x)->end());
2451                         }
2452                 }
2453         }
2454
2455         XMLNode &after = _editor->session()->locations()->get_state();
2456         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2457         _editor->commit_reversible_command ();
2458
2459         _line->hide();
2460 }
2461
2462 void
2463 MarkerDrag::update_item (Location* location)
2464 {
2465         double const x1 = _editor->frame_to_pixel (location->start());
2466
2467         _points.front().set_x(x1);
2468         _points.back().set_x(x1);
2469         _line->property_points() = _points;
2470 }
2471
2472 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2473         : Drag (e, i),
2474           _cumulative_x_drag (0),
2475           _cumulative_y_drag (0)
2476 {
2477         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2478         assert (_point);
2479 }
2480
2481
2482 void
2483 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2484 {
2485         Drag::start_grab (event, _editor->fader_cursor);
2486
2487         // start the grab at the center of the control point so
2488         // the point doesn't 'jump' to the mouse after the first drag
2489         _time_axis_view_grab_x = _point->get_x();
2490         _time_axis_view_grab_y = _point->get_y();
2491         nframes64_t grab_frame = _editor->pixel_to_frame (_time_axis_view_grab_x);
2492
2493         float const fraction = 1 - (_point->get_y() / _point->line().height());
2494
2495         _point->line().start_drag_single (_point, grab_frame, fraction);
2496
2497         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2498                                             event->button.x + 10, event->button.y + 10);
2499
2500         _editor->show_verbose_canvas_cursor ();
2501 }
2502
2503 void
2504 ControlPointDrag::motion (GdkEvent* event, bool)
2505 {
2506         double dx = current_pointer_x() - last_pointer_x();
2507         double dy = current_pointer_y() - last_pointer_y();
2508
2509         if (event->button.state & Keyboard::SecondaryModifier) {
2510                 dx *= 0.1;
2511                 dy *= 0.1;
2512         }
2513
2514         /* coordinate in TimeAxisView's space */
2515         double cx = _time_axis_view_grab_x + _cumulative_x_drag + dx;
2516         double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2517
2518         // calculate zero crossing point. back off by .01 to stay on the
2519         // positive side of zero
2520         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2521
2522         // make sure we hit zero when passing through
2523         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2524                 cy = zero_gain_y;
2525         }
2526
2527         if (_x_constrained) {
2528                 cx = _time_axis_view_grab_x;
2529         }
2530         if (_y_constrained) {
2531                 cy = _time_axis_view_grab_y;
2532         }
2533
2534         _cumulative_x_drag = cx - _time_axis_view_grab_x;
2535         _cumulative_y_drag = cy - _time_axis_view_grab_y;
2536
2537         cx = max (0.0, cx);
2538         cy = max (0.0, cy);
2539         cy = min ((double) _point->line().height(), cy);
2540
2541         nframes64_t cx_frames = _editor->unit_to_frame (cx);
2542
2543         if (!_x_constrained) {
2544                 _editor->snap_to_with_modifier (cx_frames, event);
2545         }
2546
2547         float const fraction = 1.0 - (cy / _point->line().height());
2548
2549         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2550
2551         _point->line().drag_motion (cx_frames, fraction, push);
2552
2553         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2554 }
2555
2556 void
2557 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2558 {
2559         if (!movement_occurred) {
2560
2561                 /* just a click */
2562
2563                 if ((event->type == GDK_BUTTON_RELEASE) && (event->button.button == 1) && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2564                         _editor->reset_point_selection ();
2565                 }
2566
2567         } else {
2568                 motion (event, false);
2569         }
2570         _point->line().end_drag ();
2571 }
2572
2573 bool
2574 ControlPointDrag::active (Editing::MouseMode m)
2575 {
2576         if (m == Editing::MouseGain) {
2577                 /* always active in mouse gain */
2578                 return true;
2579         }
2580
2581         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2582         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2583 }
2584
2585 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2586         : Drag (e, i),
2587           _line (0),
2588           _cumulative_y_drag (0)
2589 {
2590
2591 }
2592 void
2593 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2594 {
2595         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2596         assert (_line);
2597
2598         _item = &_line->grab_item ();
2599
2600         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2601            origin, and ditto for y.
2602         */
2603
2604         double cx = event->button.x;
2605         double cy = event->button.y;
2606
2607         _line->parent_group().w2i (cx, cy);
2608
2609         nframes64_t const frame_within_region = (nframes64_t) floor (cx * _editor->frames_per_unit);
2610
2611         uint32_t before;
2612         uint32_t after;
2613         
2614         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2615                 /* no adjacent points */
2616                 return;
2617         }
2618
2619         Drag::start_grab (event, _editor->fader_cursor);
2620
2621         /* store grab start in parent frame */
2622
2623         _time_axis_view_grab_x = cx;
2624         _time_axis_view_grab_y = cy;
2625
2626         double fraction = 1.0 - (cy / _line->height());
2627
2628         _line->start_drag_line (before, after, fraction);
2629
2630         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2631                                             event->button.x + 10, event->button.y + 10);
2632
2633         _editor->show_verbose_canvas_cursor ();
2634 }
2635
2636 void
2637 LineDrag::motion (GdkEvent* event, bool)
2638 {
2639         double dy = current_pointer_y() - last_pointer_y();
2640
2641         if (event->button.state & Keyboard::SecondaryModifier) {
2642                 dy *= 0.1;
2643         }
2644
2645         double cy = _time_axis_view_grab_y + _cumulative_y_drag + dy;
2646
2647         _cumulative_y_drag = cy - _time_axis_view_grab_y;
2648
2649         cy = max (0.0, cy);
2650         cy = min ((double) _line->height(), cy);
2651
2652         double const fraction = 1.0 - (cy / _line->height());
2653
2654         bool push;
2655
2656         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2657                 push = false;
2658         } else {
2659                 push = true;
2660         }
2661
2662         /* we are ignoring x position for this drag, so we can just pass in 0 */
2663         _line->drag_motion (0, fraction, push);
2664
2665         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2666 }
2667
2668 void
2669 LineDrag::finished (GdkEvent* event, bool)
2670 {
2671         motion (event, false);
2672         _line->end_drag ();
2673 }
2674
2675 void
2676 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2677 {
2678         Drag::start_grab (event);
2679         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2680 }
2681
2682 void
2683 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2684 {
2685         nframes64_t start;
2686         nframes64_t end;
2687         double y1;
2688         double y2;
2689
2690         /* use a bigger drag threshold than the default */
2691
2692         nframes64_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2693
2694         if (abs ((int) (pf - grab_frame())) < 8) {
2695                 return;
2696         }
2697
2698         nframes64_t grab = grab_frame ();
2699         if (Config->get_rubberbanding_snaps_to_grid ()) {
2700                 _editor->snap_to_with_modifier (grab, event);
2701         }
2702
2703         /* base start and end on initial click position */
2704
2705         if (pf < grab) {
2706                 start = pf;
2707                 end = grab;
2708         } else {
2709                 end = pf;
2710                 start = grab;
2711         }
2712
2713         if (current_pointer_y() < grab_y()) {
2714                 y1 = current_pointer_y();
2715                 y2 = grab_y();
2716         } else {
2717                 y2 = current_pointer_y();
2718                 y1 = grab_y();
2719         }
2720
2721
2722         if (start != end || y1 != y2) {
2723
2724                 double x1 = _editor->frame_to_pixel (start);
2725                 double x2 = _editor->frame_to_pixel (end);
2726
2727                 _editor->rubberband_rect->property_x1() = x1;
2728                 _editor->rubberband_rect->property_y1() = y1;
2729                 _editor->rubberband_rect->property_x2() = x2;
2730                 _editor->rubberband_rect->property_y2() = y2;
2731
2732                 _editor->rubberband_rect->show();
2733                 _editor->rubberband_rect->raise_to_top();
2734
2735                 _editor->show_verbose_time_cursor (pf, 10);
2736         }
2737 }
2738
2739 void
2740 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
2741 {
2742         if (movement_occurred) {
2743
2744                 motion (event, false);
2745
2746                 double y1,y2;
2747                 if (current_pointer_y() < grab_y()) {
2748                         y1 = current_pointer_y();
2749                         y2 = grab_y();
2750                 } else {
2751                         y2 = current_pointer_y();
2752                         y1 = grab_y();
2753                 }
2754
2755
2756                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2757                 bool committed;
2758
2759                 _editor->begin_reversible_command (_("rubberband selection"));
2760
2761                 if (grab_frame() < last_pointer_frame()) {
2762                         committed = _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op);
2763                 } else {
2764                         committed = _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op);
2765                 }
2766
2767                 if (!committed) {
2768                         _editor->commit_reversible_command ();
2769                 }
2770
2771         } else {
2772                 if (!getenv("ARDOUR_SAE")) {
2773                         _editor->selection->clear_tracks();
2774                 }
2775                 _editor->selection->clear_regions();
2776                 _editor->selection->clear_points ();
2777                 _editor->selection->clear_lines ();
2778         }
2779
2780         _editor->rubberband_rect->hide();
2781 }
2782
2783 void
2784 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2785 {
2786         Drag::start_grab (event);
2787
2788         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2789 }
2790
2791 void
2792 TimeFXDrag::motion (GdkEvent* event, bool)
2793 {
2794         RegionView* rv = _primary;
2795
2796         nframes64_t const pf = adjusted_current_frame (event);
2797
2798         if (pf == last_pointer_frame()) {
2799                 return;
2800         }
2801
2802         if (pf > rv->region()->position()) {
2803                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
2804         }
2805
2806         _editor->show_verbose_time_cursor (pf, 10);
2807 }
2808
2809 void
2810 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2811 {
2812         _primary->get_time_axis_view().hide_timestretch ();
2813
2814         if (!movement_occurred) {
2815                 return;
2816         }
2817
2818         if (last_pointer_frame() < _primary->region()->position()) {
2819                 /* backwards drag of the left edge - not usable */
2820                 return;
2821         }
2822
2823         nframes64_t newlen = last_pointer_frame() - _primary->region()->position();
2824
2825         float percentage = (double) newlen / (double) _primary->region()->length();
2826
2827 #ifndef USE_RUBBERBAND
2828         // Soundtouch uses percentage / 100 instead of normal (/ 1)
2829         if (_primary->region()->data_type() == DataType::AUDIO) {
2830                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
2831         }
2832 #endif
2833
2834         _editor->begin_reversible_command (_("timestretch"));
2835
2836         // XXX how do timeFX on multiple regions ?
2837
2838         RegionSelection rs;
2839         rs.add (_primary);
2840
2841         if (!_editor->time_stretch (rs, percentage) == 0) {
2842                 error << _("An error occurred while executing time stretch operation") << endmsg;
2843         }
2844 }
2845
2846 void
2847 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2848 {
2849         Drag::start_grab (event);
2850 }
2851
2852 void
2853 ScrubDrag::motion (GdkEvent* /*event*/, bool)
2854 {
2855         _editor->scrub ();
2856 }
2857
2858 void
2859 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
2860 {
2861         if (movement_occurred && _editor->session()) {
2862                 /* make sure we stop */
2863                 _editor->session()->request_transport_speed (0.0);
2864         }
2865 }
2866
2867 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
2868         : Drag (e, i)
2869         , _operation (o)
2870         , _copy (false)
2871         , _original_pointer_time_axis (-1)
2872         , _last_pointer_time_axis (-1)
2873 {
2874
2875 }
2876
2877 void
2878 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2879 {
2880         nframes64_t start = 0;
2881         nframes64_t end = 0;
2882
2883         if (_editor->session() == 0) {
2884                 return;
2885         }
2886
2887         Gdk::Cursor* cursor = 0;
2888
2889         switch (_operation) {
2890         case CreateSelection:
2891                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2892                         _copy = true;
2893                 } else {
2894                         _copy = false;
2895                 }
2896                 cursor = _editor->selector_cursor;
2897                 Drag::start_grab (event, cursor);
2898                 break;
2899
2900         case SelectionStartTrim:
2901                 if (_editor->clicked_axisview) {
2902                         _editor->clicked_axisview->order_selection_trims (_item, true);
2903                 }
2904                 Drag::start_grab (event, _editor->trimmer_cursor);
2905                 start = _editor->selection->time[_editor->clicked_selection].start;
2906                 _pointer_frame_offset = grab_frame() - start;
2907                 break;
2908
2909         case SelectionEndTrim:
2910                 if (_editor->clicked_axisview) {
2911                         _editor->clicked_axisview->order_selection_trims (_item, false);
2912                 }
2913                 Drag::start_grab (event, _editor->trimmer_cursor);
2914                 end = _editor->selection->time[_editor->clicked_selection].end;
2915                 _pointer_frame_offset = grab_frame() - end;
2916                 break;
2917
2918         case SelectionMove:
2919                 start = _editor->selection->time[_editor->clicked_selection].start;
2920                 Drag::start_grab (event, cursor);
2921                 _pointer_frame_offset = grab_frame() - start;
2922                 break;
2923         }
2924
2925         if (_operation == SelectionMove) {
2926                 _editor->show_verbose_time_cursor (start, 10);
2927         } else {
2928                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2929         }
2930
2931         _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
2932 }
2933
2934 void
2935 SelectionDrag::motion (GdkEvent* event, bool first_move)
2936 {
2937         nframes64_t start = 0;
2938         nframes64_t end = 0;
2939         nframes64_t length;
2940
2941         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (current_pointer_y ());
2942         if (pending_time_axis.first == 0) {
2943                 return;
2944         }
2945         
2946         nframes64_t const pending_position = adjusted_current_frame (event);
2947
2948         /* only alter selection if things have changed */
2949
2950         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
2951                 return;
2952         }
2953
2954         switch (_operation) {
2955         case CreateSelection:
2956         {
2957                 nframes64_t grab = grab_frame ();
2958
2959                 if (first_move) {
2960                         _editor->snap_to (grab);
2961                 }
2962
2963                 if (pending_position < grab_frame()) {
2964                         start = pending_position;
2965                         end = grab;
2966                 } else {
2967                         end = pending_position;
2968                         start = grab;
2969                 }
2970
2971                 /* first drag: Either add to the selection
2972                    or create a new selection
2973                 */
2974
2975                 if (first_move) {
2976
2977                         _editor->begin_reversible_command (_("range selection"));
2978                         _have_transaction = true;
2979
2980                         if (_copy) {
2981                                 /* adding to the selection */
2982                                 _editor->selection->add (_editor->clicked_axisview);
2983                                 _editor->clicked_selection = _editor->selection->add (start, end);
2984                                 _copy = false;
2985                         } else {
2986                                 /* new selection */
2987
2988                                 if (!_editor->selection->selected (_editor->clicked_axisview)) {
2989                                         _editor->selection->set (_editor->clicked_axisview);
2990                                 }
2991                                 
2992                                 _editor->clicked_selection = _editor->selection->set (start, end);
2993                         }
2994                 }
2995
2996                 /* select the track that we're in */
2997                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
2998                         _editor->selection->add (pending_time_axis.first);
2999                         _added_time_axes.push_back (pending_time_axis.first);
3000                 }
3001
3002                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3003                    tracks that we selected in the first place.
3004                 */
3005                 
3006                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3007                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3008
3009                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3010                 while (i != _added_time_axes.end()) {
3011
3012                         list<TimeAxisView*>::iterator tmp = i;
3013                         ++tmp;
3014                         
3015                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3016                                 _editor->selection->remove (*i);
3017                                 _added_time_axes.remove (*i);
3018                         }
3019
3020                         i = tmp;
3021                 }
3022
3023         }
3024         break;
3025
3026         case SelectionStartTrim:
3027
3028                 if (first_move) {
3029                         _editor->begin_reversible_command (_("trim selection start"));
3030                         _have_transaction = true;
3031                 }
3032                 
3033                 start = _editor->selection->time[_editor->clicked_selection].start;
3034                 end = _editor->selection->time[_editor->clicked_selection].end;
3035
3036                 if (pending_position > end) {
3037                         start = end;
3038                 } else {
3039                         start = pending_position;
3040                 }
3041                 break;
3042
3043         case SelectionEndTrim:
3044
3045                 if (first_move) {
3046                         _editor->begin_reversible_command (_("trim selection end"));
3047                         _have_transaction = true;
3048                 }
3049
3050                 start = _editor->selection->time[_editor->clicked_selection].start;
3051                 end = _editor->selection->time[_editor->clicked_selection].end;
3052
3053                 if (pending_position < start) {
3054                         end = start;
3055                 } else {
3056                         end = pending_position;
3057                 }
3058
3059                 break;
3060
3061         case SelectionMove:
3062
3063                 if (first_move) {
3064                         _editor->begin_reversible_command (_("move selection"));
3065                         _have_transaction = true;
3066                 }
3067
3068                 start = _editor->selection->time[_editor->clicked_selection].start;
3069                 end = _editor->selection->time[_editor->clicked_selection].end;
3070
3071                 length = end - start;
3072
3073                 start = pending_position;
3074                 _editor->snap_to (start);
3075
3076                 end = start + length;
3077
3078                 break;
3079         }
3080
3081         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3082                 _editor->start_canvas_autoscroll (1, 0);
3083         }
3084
3085         if (start != end) {
3086                 _editor->selection->replace (_editor->clicked_selection, start, end);
3087         }
3088
3089         if (_operation == SelectionMove) {
3090                 _editor->show_verbose_time_cursor(start, 10);
3091         } else {
3092                 _editor->show_verbose_time_cursor(pending_position, 10);
3093         }
3094 }
3095
3096 void
3097 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3098 {
3099         Session* s = _editor->session();
3100
3101         if (movement_occurred) {
3102                 motion (event, false);
3103                 /* XXX this is not object-oriented programming at all. ick */
3104                 if (_editor->selection->time.consolidate()) {
3105                         _editor->selection->TimeChanged ();
3106                 }
3107
3108                 if (_have_transaction) {
3109                         _editor->commit_reversible_command ();
3110                 }
3111
3112                 /* XXX what if its a music time selection? */
3113                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3114                         s->request_play_range (&_editor->selection->time, true);
3115                 }
3116
3117
3118         } else {
3119                 /* just a click, no pointer movement.*/
3120
3121                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3122                         _editor->selection->clear_time();
3123                 }
3124
3125                 if (!_editor->selection->selected (_editor->clicked_axisview)) {
3126                         _editor->selection->set (_editor->clicked_axisview);
3127                 }
3128                 
3129                 if (s && s->get_play_range () && s->transport_rolling()) {
3130                         s->request_stop (false, false);
3131                 }
3132
3133         }
3134
3135         _editor->stop_canvas_autoscroll ();
3136 }
3137
3138 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3139         : Drag (e, i),
3140           _operation (o),
3141           _copy (false)
3142 {
3143         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, _editor->physical_screen_height);
3144         _drag_rect->hide ();
3145
3146         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3147         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3148 }
3149
3150 void
3151 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3152 {
3153         if (_editor->session() == 0) {
3154                 return;
3155         }
3156
3157         Gdk::Cursor* cursor = 0;
3158
3159         if (!_editor->temp_location) {
3160                 _editor->temp_location = new Location;
3161         }
3162
3163         switch (_operation) {
3164         case CreateRangeMarker:
3165         case CreateTransportMarker:
3166         case CreateCDMarker:
3167
3168                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3169                         _copy = true;
3170                 } else {
3171                         _copy = false;
3172                 }
3173                 cursor = _editor->selector_cursor;
3174                 break;
3175         }
3176
3177         Drag::start_grab (event, cursor);
3178
3179         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3180 }
3181
3182 void
3183 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3184 {
3185         nframes64_t start = 0;
3186         nframes64_t end = 0;
3187         ArdourCanvas::SimpleRect *crect;
3188
3189         switch (_operation) {
3190         case CreateRangeMarker:
3191                 crect = _editor->range_bar_drag_rect;
3192                 break;
3193         case CreateTransportMarker:
3194                 crect = _editor->transport_bar_drag_rect;
3195                 break;
3196         case CreateCDMarker:
3197                 crect = _editor->cd_marker_bar_drag_rect;
3198                 break;
3199         default:
3200                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3201                 return;
3202                 break;
3203         }
3204
3205         nframes64_t const pf = adjusted_current_frame (event);
3206
3207         /* only alter selection if the current frame is
3208            different from the last frame position.
3209          */
3210
3211         if (pf == last_pointer_frame()) {
3212                 return;
3213         }
3214
3215         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3216                 nframes64_t grab = grab_frame ();
3217                 _editor->snap_to (grab);
3218                 
3219                 if (pf < grab_frame()) {
3220                         start = pf;
3221                         end = grab;
3222                 } else {
3223                         end = pf;
3224                         start = grab;
3225                 }
3226
3227                 /* first drag: Either add to the selection
3228                    or create a new selection.
3229                 */
3230
3231                 if (first_move) {
3232
3233                         _editor->temp_location->set (start, end);
3234
3235                         crect->show ();
3236
3237                         update_item (_editor->temp_location);
3238                         _drag_rect->show();
3239                         //_drag_rect->raise_to_top();
3240
3241                 }
3242         }
3243
3244         if (event->button.x >= _editor->horizontal_adjustment.get_value() + _editor->_canvas_width) {
3245                 _editor->start_canvas_autoscroll (1, 0);
3246         }
3247
3248         if (start != end) {
3249                 _editor->temp_location->set (start, end);
3250
3251                 double x1 = _editor->frame_to_pixel (start);
3252                 double x2 = _editor->frame_to_pixel (end);
3253                 crect->property_x1() = x1;
3254                 crect->property_x2() = x2;
3255
3256                 update_item (_editor->temp_location);
3257         }
3258
3259         _editor->show_verbose_time_cursor (pf, 10);
3260
3261 }
3262
3263 void
3264 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3265 {
3266         Location * newloc = 0;
3267         string rangename;
3268         int flags;
3269
3270         if (movement_occurred) {
3271                 motion (event, false);
3272                 _drag_rect->hide();
3273
3274                 switch (_operation) {
3275                 case CreateRangeMarker:
3276                 case CreateCDMarker:
3277                     {
3278                         _editor->begin_reversible_command (_("new range marker"));
3279                         XMLNode &before = _editor->session()->locations()->get_state();
3280                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3281                         if (_operation == CreateCDMarker) {
3282                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3283                                 _editor->cd_marker_bar_drag_rect->hide();
3284                         }
3285                         else {
3286                                 flags = Location::IsRangeMarker;
3287                                 _editor->range_bar_drag_rect->hide();
3288                         }
3289                         newloc = new Location(_editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags);
3290                         _editor->session()->locations()->add (newloc, true);
3291                         XMLNode &after = _editor->session()->locations()->get_state();
3292                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3293                         _editor->commit_reversible_command ();
3294                         break;
3295                     }
3296
3297                 case CreateTransportMarker:
3298                         // popup menu to pick loop or punch
3299                         _editor->new_transport_marker_context_menu (&event->button, _item);
3300                         break;
3301                 }
3302         } else {
3303                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3304
3305                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3306
3307                         nframes64_t start;
3308                         nframes64_t end;
3309
3310                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3311
3312                         if (end == max_frames) {
3313                                 end = _editor->session()->current_end_frame ();
3314                         }
3315
3316                         if (start == max_frames) {
3317                                 start = _editor->session()->current_start_frame ();
3318                         }
3319
3320                         switch (_editor->mouse_mode) {
3321                         case MouseObject:
3322                                 /* find the two markers on either side and then make the selection from it */
3323                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set);
3324                                 break;
3325
3326                         case MouseRange:
3327                                 /* find the two markers on either side of the click and make the range out of it */
3328                                 _editor->selection->set (start, end);
3329                                 break;
3330
3331                         default:
3332                                 break;
3333                         }
3334                 }
3335         }
3336
3337         _editor->stop_canvas_autoscroll ();
3338 }
3339
3340
3341
3342 void
3343 RangeMarkerBarDrag::update_item (Location* location)
3344 {
3345         double const x1 = _editor->frame_to_pixel (location->start());
3346         double const x2 = _editor->frame_to_pixel (location->end());
3347
3348         _drag_rect->property_x1() = x1;
3349         _drag_rect->property_x2() = x2;
3350 }
3351
3352 void
3353 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3354 {
3355         Drag::start_grab (event, _editor->zoom_cursor);
3356         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3357 }
3358
3359 void
3360 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3361 {
3362         nframes64_t start;
3363         nframes64_t end;
3364
3365         nframes64_t const pf = adjusted_current_frame (event);
3366
3367         if (pf == last_pointer_frame()) {
3368                 return;
3369         }
3370
3371         nframes64_t grab = grab_frame ();
3372         _editor->snap_to_with_modifier (grab, event);
3373
3374         /* base start and end on initial click position */
3375         if (pf < grab) {
3376                 start = pf;
3377                 end = grab;
3378         } else {
3379                 end = pf;
3380                 start = grab;
3381         }
3382
3383         if (start != end) {
3384
3385                 if (first_move) {
3386                         _editor->zoom_rect->show();
3387                         _editor->zoom_rect->raise_to_top();
3388                 }
3389
3390                 _editor->reposition_zoom_rect(start, end);
3391
3392                 _editor->show_verbose_time_cursor (pf, 10);
3393         }
3394 }
3395
3396 void
3397 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3398 {
3399         if (movement_occurred) {
3400                 motion (event, false);
3401
3402                 if (grab_frame() < last_pointer_frame()) {
3403                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3404                 } else {
3405                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3406                 }
3407         } else {
3408                 _editor->temporal_zoom_to_frame (false, grab_frame());
3409                 /*
3410                 temporal_zoom_step (false);
3411                 center_screen (grab_frame());
3412                 */
3413         }
3414
3415         _editor->zoom_rect->hide();
3416 }
3417
3418 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3419         : Drag (e, i)
3420 {
3421         CanvasNoteEvent*     cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3422         region = &cnote->region_view();
3423 }
3424
3425 void
3426 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3427 {
3428         Drag::start_grab (event);
3429
3430         drag_delta_x = 0;
3431         drag_delta_note = 0;
3432
3433         double event_x;
3434         double event_y;
3435
3436         event_x = current_pointer_x();
3437         event_y = current_pointer_y();
3438
3439         _item->property_parent().get_value()->w2i(event_x, event_y);
3440
3441         last_x = region->snap_to_pixel(event_x);
3442         last_y = event_y;
3443
3444         CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3445
3446         if (!(was_selected = cnote->selected())) {
3447
3448                 /* tertiary-click means extend selection - we'll do that on button release,
3449                    so don't add it here, because otherwise we make it hard to figure
3450                    out the "extend-to" range.
3451                 */
3452
3453                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3454
3455                 if (!extend) {
3456                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3457
3458                         if (add) {
3459                                 region->note_selected (cnote, true);
3460                         } else {
3461                                 region->unique_select (cnote);
3462                         }
3463                 }
3464         }
3465 }
3466
3467 void
3468 NoteDrag::motion (GdkEvent*, bool)
3469 {
3470         MidiStreamView* streamview = region->midi_stream_view();
3471         double event_x;
3472         double event_y;
3473
3474         event_x = current_pointer_x();
3475         event_y = current_pointer_y();
3476
3477         _item->property_parent().get_value()->w2i(event_x, event_y);
3478
3479         event_x = region->snap_to_pixel(event_x);
3480
3481         double dx     = event_x - last_x;
3482         double dy     = event_y - last_y;
3483         last_x = event_x;
3484
3485         drag_delta_x += dx;
3486
3487         // Snap to note rows
3488
3489         if (abs (dy) < streamview->note_height()) {
3490                 dy = 0.0;
3491         } else {
3492                 int8_t this_delta_note;
3493                 if (dy > 0) {
3494                         this_delta_note = (int8_t)ceil(dy / streamview->note_height() / 2.0);
3495                 } else {
3496                         this_delta_note = (int8_t)floor(dy / streamview->note_height() / 2.0);
3497                 }
3498                 drag_delta_note -= this_delta_note;
3499                 dy = streamview->note_height() * this_delta_note;
3500                 last_y = last_y + dy;
3501         }
3502
3503         if (dx || dy) {
3504                 region->move_selection (dx, dy);
3505
3506                 CanvasNoteEvent* cnote = dynamic_cast<CanvasNoteEvent*>(_item);
3507                 char buf[4];
3508                 snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note);
3509                 //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note()));
3510                 _editor->show_verbose_canvas_cursor_with (buf);
3511         }
3512 }
3513
3514 void
3515 NoteDrag::finished (GdkEvent* ev, bool moved)
3516 {
3517         ArdourCanvas::CanvasNote* cnote = dynamic_cast<ArdourCanvas::CanvasNote*>(_item);
3518
3519         if (!moved) {
3520                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3521
3522                         if (was_selected) {
3523                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3524                                 if (add) {
3525                                         region->note_deselected (cnote);
3526                                 }
3527                         } else {
3528                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3529                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3530
3531                                 if (!extend && !add && region->selection_size() > 1) {
3532                                         region->unique_select(cnote);
3533                                 } else if (extend) {
3534                                         region->note_selected (cnote, true, true);
3535                                 } else {
3536                                         /* it was added during button press */
3537                                 }
3538                         }
3539                 }
3540         } else {
3541                 region->note_dropped (cnote, drag_delta_x, drag_delta_note);
3542         }
3543 }
3544
3545 AutomationRangeDrag::AutomationRangeDrag (Editor* e, ArdourCanvas::Item* i, list<AudioRange> const & r)
3546         : Drag (e, i)
3547         , _ranges (r)
3548         , _nothing_to_drag (false)
3549 {
3550         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3551         assert (_atav);
3552
3553         _line = _atav->line ();
3554 }
3555
3556 void
3557 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3558 {
3559         Drag::start_grab (event, cursor);
3560
3561         list<ControlPoint*> points;
3562
3563         XMLNode* state = &_line->get_state ();
3564         
3565         if (_ranges.empty()) {
3566                 
3567                 uint32_t const N = _line->npoints ();
3568                 for (uint32_t i = 0; i < N; ++i) {
3569                         points.push_back (_line->nth (i));
3570                 }
3571                 
3572         } else {
3573
3574                 boost::shared_ptr<AutomationList> the_list = _line->the_list ();
3575                 for (list<AudioRange>::const_iterator j = _ranges.begin(); j != _ranges.end(); ++j) {
3576
3577                         /* fade into and out of the region that we're dragging;
3578                            64 samples length plucked out of thin air.
3579                         */
3580                         nframes64_t const h = (j->start + j->end) / 2;
3581                         nframes64_t a = j->start + 64;
3582                         if (a > h) {
3583                                 a = h;
3584                         }
3585                         nframes64_t b = j->end - 64;
3586                         if (b < h) {
3587                                 b = h;
3588                         }
3589                         
3590                         the_list->add (j->start, the_list->eval (j->start));
3591                         _line->add_always_in_view (j->start);
3592                         the_list->add (a, the_list->eval (a));
3593                         _line->add_always_in_view (a);
3594                         the_list->add (b, the_list->eval (b));
3595                         _line->add_always_in_view (b);
3596                         the_list->add (j->end, the_list->eval (j->end));
3597                         _line->add_always_in_view (j->end);
3598                 }
3599
3600                 uint32_t const N = _line->npoints ();
3601                 for (uint32_t i = 0; i < N; ++i) {
3602
3603                         ControlPoint* p = _line->nth (i);
3604
3605                         list<AudioRange>::const_iterator j = _ranges.begin ();
3606                         while (j != _ranges.end() && (j->start >= (*p->model())->when || j->end <= (*p->model())->when)) {
3607                                 ++j;
3608                         }
3609
3610                         if (j != _ranges.end()) {
3611                                 points.push_back (p);
3612                         }
3613                 }
3614         }
3615
3616         if (points.empty()) {
3617                 _nothing_to_drag = true;
3618                 return;
3619         }
3620
3621         _line->start_drag_multiple (points, 1 - (current_pointer_y() / _line->height ()), state);
3622 }
3623
3624 void
3625 AutomationRangeDrag::motion (GdkEvent* event, bool first_move)
3626 {
3627         if (_nothing_to_drag) {
3628                 return;
3629         }
3630         
3631         float const f = 1 - (current_pointer_y() / _line->height());
3632
3633         /* we are ignoring x position for this drag, so we can just pass in 0 */
3634         _line->drag_motion (0, f, false);
3635 }
3636
3637 void
3638 AutomationRangeDrag::finished (GdkEvent* event, bool)
3639 {
3640         if (_nothing_to_drag) {
3641                 return;
3642         }
3643         
3644         motion (event, false);
3645         _line->end_drag ();
3646         _line->clear_always_in_view ();
3647 }