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