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