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