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