Patch from Lincoln to fix uncommitted reversible command during rubberband selection...
[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);
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 () - _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 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1181         : RegionMotionDrag (e, i, p, v, b),
1182           _copy (c)
1183 {
1184         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1185         
1186         double speed = 1;
1187         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1188         if (rtv && rtv->is_track()) {
1189                 speed = rtv->track()->speed ();
1190         }
1191
1192         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1193 }
1194
1195 void
1196 RegionMoveDrag::setup_pointer_frame_offset ()
1197 {
1198         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1199 }
1200
1201 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1202         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1203 {
1204         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1205         
1206         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1207                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1208
1209         _primary = v->view()->create_region_view (r, false, false);
1210
1211         _primary->get_canvas_group()->show ();
1212         _primary->set_position (pos, 0);
1213         _views.push_back (DraggingView (_primary, this));
1214
1215         _last_frame_position = pos;
1216
1217         _item = _primary->get_canvas_group ();
1218 }
1219
1220 void
1221 RegionInsertDrag::finished (GdkEvent *, bool)
1222 {
1223         _editor->update_canvas_now ();
1224
1225         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1226
1227         _primary->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
1228         _primary->get_canvas_group()->property_y() = 0;
1229
1230         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1231
1232         _editor->begin_reversible_command (Operations::insert_region);
1233         playlist->clear_changes ();
1234         playlist->add_region (_primary->region (), _last_frame_position);
1235         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1236         _editor->commit_reversible_command ();
1237
1238         delete _primary;
1239         _primary = 0;
1240         _views.clear ();
1241 }
1242
1243 void
1244 RegionInsertDrag::aborted (bool)
1245 {
1246         delete _primary;
1247         _primary = 0;
1248         _views.clear ();
1249 }
1250
1251 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1252         : RegionMoveDrag (e, i, p, v, false, false)
1253 {
1254         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1255 }
1256
1257 struct RegionSelectionByPosition {
1258     bool operator() (RegionView*a, RegionView* b) {
1259             return a->region()->position () < b->region()->position();
1260     }
1261 };
1262
1263 void
1264 RegionSpliceDrag::motion (GdkEvent* event, bool)
1265 {
1266         /* Which trackview is this ? */
1267
1268         pair<TimeAxisView*, int> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
1269         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1270         layer_t layer = tvp.second;
1271
1272         if (tv && tv->layer_display() == Overlaid) {
1273                 layer = 0;
1274         }
1275
1276         /* The region motion is only processed if the pointer is over
1277            an audio track.
1278         */
1279
1280         if (!tv || !tv->is_track()) {
1281                 /* To make sure we hide the verbose canvas cursor when the mouse is
1282                    not held over and audiotrack.
1283                 */
1284                 _editor->hide_verbose_canvas_cursor ();
1285                 return;
1286         }
1287
1288         int dir;
1289
1290         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1291                 dir = 1;
1292         } else {
1293                 dir = -1;
1294         }
1295
1296         RegionSelection copy (_editor->selection->regions);
1297
1298         RegionSelectionByPosition cmp;
1299         copy.sort (cmp);
1300
1301         framepos_t const pf = adjusted_current_frame (event);
1302
1303         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1304
1305                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1306
1307                 if (!atv) {
1308                         continue;
1309                 }
1310
1311                 boost::shared_ptr<Playlist> playlist;
1312
1313                 if ((playlist = atv->playlist()) == 0) {
1314                         continue;
1315                 }
1316
1317                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1318                         continue;
1319                 }
1320
1321                 if (dir > 0) {
1322                         if (pf < (*i)->region()->last_frame() + 1) {
1323                                 continue;
1324                         }
1325                 } else {
1326                         if (pf > (*i)->region()->first_frame()) {
1327                                 continue;
1328                         }
1329                 }
1330
1331
1332                 playlist->shuffle ((*i)->region(), dir);
1333         }
1334 }
1335
1336 void
1337 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1338 {
1339         RegionMoveDrag::finished (event, movement_occurred);
1340 }
1341
1342 void
1343 RegionSpliceDrag::aborted (bool)
1344 {
1345         /* XXX: TODO */
1346 }
1347
1348 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1349         : Drag (e, i),
1350           _view (dynamic_cast<MidiTimeAxisView*> (v))
1351 {
1352         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1353         
1354         assert (_view);
1355 }
1356
1357 void
1358 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1359 {
1360         if (first_move) {
1361                 add_region();
1362         } else {
1363                 if (_region) {
1364                         framepos_t const f = adjusted_current_frame (event);
1365                         if (f < grab_frame()) {
1366                                 _region->set_position (f, this);
1367                         }
1368                         
1369                         /* again, don't use a zero-length region (see above) */
1370                         framecnt_t const len = abs (f - grab_frame ());
1371                         _region->set_length (len < 1 ? 1 : len, this);
1372                 }
1373         }
1374 }
1375
1376 void
1377 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1378 {
1379         if (!movement_occurred) {
1380                 add_region ();
1381         }
1382
1383         if (_region) {
1384                 _editor->commit_reversible_command ();
1385         }
1386 }
1387
1388 void
1389 RegionCreateDrag::add_region ()
1390 {
1391         if (_editor->session()) {
1392                 const TempoMap& map (_editor->session()->tempo_map());
1393                 framecnt_t pos = grab_frame();
1394                 const Meter& m = map.meter_at (pos);
1395                 /* not that the frame rate used here can be affected by pull up/down which
1396                    might be wrong.
1397                 */
1398                 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
1399                 _region = _view->add_region (grab_frame(), len, false);
1400         }
1401 }
1402
1403 void
1404 RegionCreateDrag::aborted (bool)
1405 {
1406         /* XXX */
1407 }
1408
1409 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1410         : Drag (e, i)
1411         , region (0)
1412 {
1413         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1414 }
1415
1416 void
1417 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1418 {
1419         Gdk::Cursor* cursor;
1420         ArdourCanvas::CanvasNoteEvent* cnote = dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item);
1421         float x_fraction = cnote->mouse_x_fraction ();
1422
1423         if (x_fraction > 0.0 && x_fraction < 0.25) {
1424                 cursor = _editor->cursors()->left_side_trim;
1425         } else  {
1426                 cursor = _editor->cursors()->right_side_trim;
1427         }
1428
1429         Drag::start_grab (event, cursor);
1430
1431         region = &cnote->region_view();
1432
1433         double const region_start = region->get_position_pixels();
1434         double const middle_point = region_start + cnote->x1() + (cnote->x2() - cnote->x1()) / 2.0L;
1435
1436         if (grab_x() <= middle_point) {
1437                 cursor = _editor->cursors()->left_side_trim;
1438                 at_front = true;
1439         } else {
1440                 cursor = _editor->cursors()->right_side_trim;
1441                 at_front = false;
1442         }
1443
1444         _item->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, *cursor, event->motion.time);
1445
1446         if (event->motion.state & Keyboard::PrimaryModifier) {
1447                 relative = false;
1448         } else {
1449                 relative = true;
1450         }
1451
1452         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1453
1454         if (ms.size() > 1) {
1455                 /* has to be relative, may make no sense otherwise */
1456                 relative = true;
1457         }
1458
1459         /* select this note; if it is already selected, preserve the existing selection,
1460            otherwise make this note the only one selected.
1461         */
1462         region->note_selected (cnote, cnote->selected ());
1463
1464         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1465                 MidiRegionSelection::iterator next;
1466                 next = r;
1467                 ++next;
1468                 (*r)->begin_resizing (at_front);
1469                 r = next;
1470         }
1471 }
1472
1473 void
1474 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1475 {
1476         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1477         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1478                 (*r)->update_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1479         }
1480 }
1481
1482 void
1483 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1484 {
1485         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1486         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1487                 (*r)->commit_resizing (dynamic_cast<ArdourCanvas::CanvasNoteEvent*>(_item), at_front, _drags->current_pointer_x() - grab_x(), relative);
1488         }
1489 }
1490
1491 void
1492 NoteResizeDrag::aborted (bool)
1493 {
1494         /* XXX: TODO */
1495 }
1496
1497 RegionGainDrag::RegionGainDrag (Editor* e, ArdourCanvas::Item* i)
1498         : Drag (e, i)
1499 {
1500         DEBUG_TRACE (DEBUG::Drags, "New RegionGainDrag\n");
1501 }
1502
1503 void
1504 RegionGainDrag::motion (GdkEvent* /*event*/, bool)
1505 {
1506
1507 }
1508
1509 void
1510 RegionGainDrag::finished (GdkEvent *, bool)
1511 {
1512
1513 }
1514
1515 void
1516 RegionGainDrag::aborted (bool)
1517 {
1518         /* XXX: TODO */
1519 }
1520
1521 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1522         : RegionDrag (e, i, p, v)
1523 {
1524         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
1525 }
1526
1527 void
1528 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
1529 {
1530         double speed = 1.0;
1531         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1532         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1533
1534         if (tv && tv->is_track()) {
1535                 speed = tv->track()->speed();
1536         }
1537
1538         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
1539         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
1540         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
1541
1542         framepos_t const pf = adjusted_current_frame (event);
1543
1544         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1545                 /* Move the contents of the region around without changing the region bounds */
1546                 _operation = ContentsTrim;
1547                 Drag::start_grab (event, _editor->cursors()->trimmer);
1548         } else {
1549                 /* These will get overridden for a point trim.*/
1550                 if (pf < (region_start + region_length/2)) {
1551                         /* closer to front */
1552                         _operation = StartTrim;
1553                         Drag::start_grab (event, _editor->cursors()->left_side_trim);
1554                 } else {
1555                         /* closer to end */
1556                         _operation = EndTrim;
1557                         Drag::start_grab (event, _editor->cursors()->right_side_trim);
1558                 }
1559         }
1560
1561         switch (_operation) {
1562         case StartTrim:
1563                 _editor->show_verbose_time_cursor (region_start, 10);
1564                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1565                         i->view->trim_front_starting ();
1566                 }
1567                 break;
1568         case EndTrim:
1569                 _editor->show_verbose_time_cursor (region_end, 10);
1570                 break;
1571         case ContentsTrim:
1572                 _editor->show_verbose_time_cursor (pf, 10);
1573                 break;
1574         }
1575
1576         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1577                 i->view->region()->suspend_property_changes ();
1578         }
1579 }
1580
1581 void
1582 TrimDrag::motion (GdkEvent* event, bool first_move)
1583 {
1584         RegionView* rv = _primary;
1585
1586         double speed = 1.0;
1587         TimeAxisView* tvp = &_primary->get_time_axis_view ();
1588         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
1589         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
1590
1591         if (tv && tv->is_track()) {
1592                 speed = tv->track()->speed();
1593         }
1594
1595         framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
1596
1597         if (first_move) {
1598
1599                 string trim_type;
1600
1601                 switch (_operation) {
1602                 case StartTrim:
1603                         trim_type = "Region start trim";
1604                         break;
1605                 case EndTrim:
1606                         trim_type = "Region end trim";
1607                         break;
1608                 case ContentsTrim:
1609                         trim_type = "Region content trim";
1610                         break;
1611                 }
1612
1613                 _editor->begin_reversible_command (trim_type);
1614
1615                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1616                         RegionView* rv = i->view;
1617                         rv->fake_set_opaque (false);
1618                         rv->enable_display (false);
1619                         rv->region()->playlist()->clear_owned_changes ();
1620
1621                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
1622
1623                         if (arv) {
1624                                 arv->temporarily_hide_envelope ();
1625                         }
1626
1627                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
1628                         insert_result = _editor->motion_frozen_playlists.insert (pl);
1629
1630                         if (insert_result.second) {
1631                                 pl->freeze();
1632                         }
1633                 }
1634         }
1635
1636         bool non_overlap_trim = false;
1637
1638         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1639                 non_overlap_trim = true;
1640         }
1641
1642         switch (_operation) {
1643         case StartTrim:
1644                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1645                         i->view->trim_front (i->initial_position + dt, non_overlap_trim);
1646                 }
1647                 break;
1648
1649         case EndTrim:
1650                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1651                         i->view->trim_end (i->initial_end + dt, non_overlap_trim);
1652                 }
1653                 break;
1654
1655         case ContentsTrim:
1656                 {
1657                         bool swap_direction = false;
1658
1659                         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1660                                 swap_direction = true;
1661                         }
1662
1663                         framecnt_t frame_delta = 0;
1664                         
1665                         bool left_direction = false;
1666                         if (last_pointer_frame() > adjusted_current_frame(event)) {
1667                                 left_direction = true;
1668                         }
1669
1670                         if (left_direction) {
1671                                 frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
1672                         } else {
1673                                 frame_delta = (adjusted_current_frame(event) - last_pointer_frame());
1674                         }
1675
1676                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1677                                 i->view->trim_contents (frame_delta, left_direction, swap_direction);
1678                         }
1679                 }
1680                 break;
1681         }
1682
1683         switch (_operation) {
1684         case StartTrim:
1685                 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->position() / speed), 10);
1686                 break;
1687         case EndTrim:
1688                 _editor->show_verbose_time_cursor ((framepos_t) (rv->region()->last_frame() / speed), 10);
1689                 break;
1690         case ContentsTrim:
1691                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1692                 break;
1693         }
1694 }
1695
1696
1697 void
1698 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
1699 {
1700         if (movement_occurred) {
1701                 motion (event, false);
1702
1703                 /* This must happen before the region's StatefulDiffCommand is created, as it may
1704                    `correct' (ahem) the region's _start from being negative to being zero.  It
1705                    needs to be zero in the undo record.
1706                 */
1707                 if (_operation == StartTrim) {
1708                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1709                                 i->view->trim_front_ending ();
1710                         }
1711                 }
1712                 
1713                 if (!_editor->selection->selected (_primary)) {
1714                         _primary->thaw_after_trim ();
1715                 } else {
1716
1717                         set<boost::shared_ptr<Playlist> > diffed_playlists;
1718
1719                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1720                                 i->view->thaw_after_trim ();
1721                                 i->view->enable_display (true);
1722                                 i->view->fake_set_opaque (true);
1723
1724                                 /* Trimming one region may affect others on the playlist, so we need
1725                                    to get undo Commands from the whole playlist rather than just the
1726                                    region.  Use diffed_playlists to make sure we don't diff a given
1727                                    playlist more than once.
1728                                 */
1729                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
1730                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
1731                                         vector<Command*> cmds;
1732                                         p->rdiff (cmds);
1733                                         _editor->session()->add_commands (cmds);
1734                                         diffed_playlists.insert (p);
1735                                 }
1736                         }
1737                 }
1738                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
1739                         (*p)->thaw ();
1740                 }
1741
1742                 _editor->motion_frozen_playlists.clear ();
1743                 _editor->commit_reversible_command();
1744
1745         } else {
1746                 /* no mouse movement */
1747                 _editor->point_trim (event, adjusted_current_frame (event));
1748         }
1749
1750         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1751                 if (_operation == StartTrim) {
1752                         i->view->trim_front_ending ();
1753                 }
1754                 
1755                 i->view->region()->resume_property_changes ();
1756         }
1757 }
1758
1759 void
1760 TrimDrag::aborted (bool movement_occurred)
1761 {
1762         /* Our motion method is changing model state, so use the Undo system
1763            to cancel.  Perhaps not ideal, as this will leave an Undo point
1764            behind which may be slightly odd from the user's point of view.
1765         */
1766
1767         finished (0, true);
1768         
1769         if (movement_occurred) {
1770                 _editor->undo ();
1771         }
1772
1773         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1774                 i->view->region()->resume_property_changes ();
1775         }
1776 }
1777
1778 void
1779 TrimDrag::setup_pointer_frame_offset ()
1780 {
1781         list<DraggingView>::iterator i = _views.begin ();
1782         while (i != _views.end() && i->view != _primary) {
1783                 ++i;
1784         }
1785
1786         if (i == _views.end()) {
1787                 return;
1788         }
1789
1790         switch (_operation) {
1791         case StartTrim:
1792                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
1793                 break;
1794         case EndTrim:
1795                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
1796                 break;
1797         case ContentsTrim:
1798                 break;
1799         }
1800 }
1801
1802 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1803         : Drag (e, i),
1804           _copy (c)
1805 {
1806         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
1807         
1808         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
1809         assert (_marker);
1810 }
1811
1812 void
1813 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1814 {
1815         if (_copy) {
1816                 // create a dummy marker for visual representation of moving the copy.
1817                 // The actual copying is not done before we reach the finish callback.
1818                 char name[64];
1819                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().beats_per_bar(), _marker->meter().note_divisor ());
1820
1821                 MeterMarker* new_marker = new MeterMarker (
1822                         *_editor,
1823                         *_editor->meter_group,
1824                         ARDOUR_UI::config()->canvasvar_MeterMarker.get(),
1825                         name,
1826                         *new MeterSection (_marker->meter())
1827                         );
1828
1829                 _item = &new_marker->the_item ();
1830                 _marker = new_marker;
1831
1832         } else {
1833
1834                 MetricSection& section (_marker->meter());
1835
1836                 if (!section.movable()) {
1837                         return;
1838                 }
1839
1840         }
1841
1842         Drag::start_grab (event, cursor);
1843
1844         _editor->show_verbose_time_cursor (adjusted_current_frame(event), 10);
1845 }
1846
1847 void
1848 MeterMarkerDrag::setup_pointer_frame_offset ()
1849 {
1850         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
1851 }
1852
1853 void
1854 MeterMarkerDrag::motion (GdkEvent* event, bool)
1855 {
1856         framepos_t const pf = adjusted_current_frame (event);
1857
1858         _marker->set_position (pf);
1859         
1860         _editor->show_verbose_time_cursor (pf, 10);
1861 }
1862
1863 void
1864 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1865 {
1866         if (!movement_occurred) {
1867                 return;
1868         }
1869
1870         motion (event, false);
1871
1872         Timecode::BBT_Time when;
1873
1874         TempoMap& map (_editor->session()->tempo_map());
1875         map.bbt_time (last_pointer_frame(), when);
1876
1877         if (_copy == true) {
1878                 _editor->begin_reversible_command (_("copy meter mark"));
1879                 XMLNode &before = map.get_state();
1880                 map.add_meter (_marker->meter(), when);
1881                 XMLNode &after = map.get_state();
1882                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1883                 _editor->commit_reversible_command ();
1884
1885                 // delete the dummy marker we used for visual representation of copying.
1886                 // a new visual marker will show up automatically.
1887                 delete _marker;
1888         } else {
1889                 _editor->begin_reversible_command (_("move meter mark"));
1890                 XMLNode &before = map.get_state();
1891                 map.move_meter (_marker->meter(), when);
1892                 XMLNode &after = map.get_state();
1893                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
1894                 _editor->commit_reversible_command ();
1895         }
1896 }
1897
1898 void
1899 MeterMarkerDrag::aborted (bool)
1900 {
1901         _marker->set_position (_marker->meter().frame ());
1902 }
1903
1904 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
1905         : Drag (e, i),
1906           _copy (c)
1907 {
1908         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
1909         
1910         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
1911         assert (_marker);
1912 }
1913
1914 void
1915 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
1916 {
1917         if (_copy) {
1918
1919                 // create a dummy marker for visual representation of moving the copy.
1920                 // The actual copying is not done before we reach the finish callback.
1921                 char name[64];
1922                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
1923
1924                 TempoMarker* new_marker = new TempoMarker (
1925                         *_editor,
1926                         *_editor->tempo_group,
1927                         ARDOUR_UI::config()->canvasvar_TempoMarker.get(),
1928                         name,
1929                         *new TempoSection (_marker->tempo())
1930                         );
1931
1932                 _item = &new_marker->the_item ();
1933                 _marker = new_marker;
1934
1935         }
1936
1937         Drag::start_grab (event, cursor);
1938
1939         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
1940 }
1941
1942 void
1943 TempoMarkerDrag::setup_pointer_frame_offset ()
1944 {
1945         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
1946 }       
1947
1948 void
1949 TempoMarkerDrag::motion (GdkEvent* event, bool)
1950 {
1951         framepos_t const pf = adjusted_current_frame (event);
1952         _marker->set_position (pf);
1953         _editor->show_verbose_time_cursor (pf, 10);
1954 }
1955
1956 void
1957 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
1958 {
1959         if (!movement_occurred) {
1960                 return;
1961         }
1962
1963         motion (event, false);
1964
1965         Timecode::BBT_Time when;
1966
1967         TempoMap& map (_editor->session()->tempo_map());
1968         map.bbt_time (last_pointer_frame(), when);
1969
1970         if (_copy == true) {
1971                 _editor->begin_reversible_command (_("copy tempo mark"));
1972                 XMLNode &before = map.get_state();
1973                 map.add_tempo (_marker->tempo(), when);
1974                 XMLNode &after = map.get_state();
1975                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1976                 _editor->commit_reversible_command ();
1977
1978                 // delete the dummy marker we used for visual representation of copying.
1979                 // a new visual marker will show up automatically.
1980                 delete _marker;
1981         } else {
1982                 _editor->begin_reversible_command (_("move tempo mark"));
1983                 XMLNode &before = map.get_state();
1984                 map.move_tempo (_marker->tempo(), when);
1985                 XMLNode &after = map.get_state();
1986                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
1987                 _editor->commit_reversible_command ();
1988         }
1989 }
1990
1991 void
1992 TempoMarkerDrag::aborted (bool)
1993 {
1994         _marker->set_position (_marker->tempo().frame());
1995 }
1996
1997 CursorDrag::CursorDrag (Editor* e, ArdourCanvas::Item* i, bool s)
1998         : Drag (e, i),
1999           _stop (s)
2000 {
2001         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2002 }
2003
2004 /** Do all the things we do when dragging the playhead to make it look as though
2005  *  we have located, without actually doing the locate (because that would cause
2006  *  the diskstream buffers to be refilled, which is too slow).
2007  */
2008 void
2009 CursorDrag::fake_locate (framepos_t t)
2010 {
2011         _editor->playhead_cursor->set_position (t);
2012         
2013         Session* s = _editor->session ();
2014         if (s->timecode_transmission_suspended ()) {
2015                 framepos_t const f = _editor->playhead_cursor->current_frame;
2016                 s->send_mmc_locate (f);
2017                 s->send_full_time_code (f);
2018         }
2019
2020         _editor->show_verbose_time_cursor (t, 10);
2021         _editor->UpdateAllTransportClocks (t);
2022 }
2023
2024 void
2025 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2026 {
2027         Drag::start_grab (event, c);
2028
2029         framepos_t where = _editor->event_frame (event, 0, 0);
2030         _editor->snap_to_with_modifier (where, event);
2031
2032         _editor->_dragging_playhead = true;
2033         
2034         Session* s = _editor->session ();
2035         
2036         if (s) {
2037                 if (_was_rolling && _stop) {
2038                         s->request_stop ();
2039                 }
2040                 
2041                 if (s->is_auditioning()) {
2042                         s->cancel_audition ();
2043                 }
2044                 
2045                 s->request_suspend_timecode_transmission ();
2046                 while (!s->timecode_transmission_suspended ()) {
2047                         /* twiddle our thumbs */
2048                 }
2049         }
2050         
2051         fake_locate (where);
2052 }
2053
2054 void
2055 CursorDrag::motion (GdkEvent* event, bool)
2056 {
2057         framepos_t const adjusted_frame = adjusted_current_frame (event);
2058
2059         if (adjusted_frame == last_pointer_frame()) {
2060                 return;
2061         }
2062
2063         fake_locate (adjusted_frame);
2064         
2065 #ifdef GTKOSX
2066         _editor->update_canvas_now ();
2067 #endif
2068 }
2069
2070 void
2071 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2072 {
2073         _editor->_dragging_playhead = false;
2074
2075         if (!movement_occurred && _stop) {
2076                 return;
2077         }
2078
2079         motion (event, false);
2080
2081         Session* s = _editor->session ();
2082         if (s) {
2083                 s->request_locate (_editor->playhead_cursor->current_frame, _was_rolling);
2084                 _editor->_pending_locate_request = true;
2085                 s->request_resume_timecode_transmission ();
2086         }
2087 }
2088
2089 void
2090 CursorDrag::aborted (bool)
2091 {
2092         if (_editor->_dragging_playhead) {
2093                 _editor->session()->request_resume_timecode_transmission ();
2094                 _editor->_dragging_playhead = false;
2095         }
2096         
2097         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2098 }
2099
2100 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2101         : RegionDrag (e, i, p, v)
2102 {
2103         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2104 }
2105
2106 void
2107 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2108 {
2109         Drag::start_grab (event, cursor);
2110
2111         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2112         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2113
2114         _editor->show_verbose_duration_cursor (r->position(), r->position() + r->fade_in()->back()->when, 10);
2115         
2116         arv->show_fade_line((framepos_t) r->fade_in()->back()->when);
2117 }
2118
2119 void
2120 FadeInDrag::setup_pointer_frame_offset ()
2121 {
2122         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2123         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2124         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2125 }
2126
2127 void
2128 FadeInDrag::motion (GdkEvent* event, bool)
2129 {
2130         framecnt_t fade_length;
2131
2132         framepos_t const pos = adjusted_current_frame (event);
2133
2134         boost::shared_ptr<Region> region = _primary->region ();
2135
2136         if (pos < (region->position() + 64)) {
2137                 fade_length = 64; // this should be a minimum defined somewhere
2138         } else if (pos > region->last_frame()) {
2139                 fade_length = region->length();
2140         } else {
2141                 fade_length = pos - region->position();
2142         }
2143
2144         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2145
2146                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2147
2148                 if (!tmp) {
2149                         continue;
2150                 }
2151
2152                 tmp->reset_fade_in_shape_width (fade_length);
2153                 tmp->show_fade_line((framecnt_t) fade_length);
2154         }
2155
2156         _editor->show_verbose_duration_cursor (region->position(), region->position() + fade_length, 10);
2157 }
2158
2159 void
2160 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2161 {
2162         if (!movement_occurred) {
2163                 return;
2164         }
2165
2166         framecnt_t fade_length;
2167
2168         framepos_t const pos = adjusted_current_frame (event);
2169
2170         boost::shared_ptr<Region> region = _primary->region ();
2171
2172         if (pos < (region->position() + 64)) {
2173                 fade_length = 64; // this should be a minimum defined somewhere
2174         } else if (pos > region->last_frame()) {
2175                 fade_length = region->length();
2176         } else {
2177                 fade_length = pos - region->position();
2178         }
2179
2180         _editor->begin_reversible_command (_("change fade in length"));
2181
2182         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2183
2184                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2185
2186                 if (!tmp) {
2187                         continue;
2188                 }
2189
2190                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2191                 XMLNode &before = alist->get_state();
2192
2193                 tmp->audio_region()->set_fade_in_length (fade_length);
2194                 tmp->audio_region()->set_fade_in_active (true);
2195                 tmp->hide_fade_line();
2196
2197                 XMLNode &after = alist->get_state();
2198                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2199         }
2200
2201         _editor->commit_reversible_command ();
2202 }
2203
2204 void
2205 FadeInDrag::aborted (bool)
2206 {
2207         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2208                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2209
2210                 if (!tmp) {
2211                         continue;
2212                 }
2213
2214                 tmp->reset_fade_in_shape_width (tmp->audio_region()->fade_in()->back()->when);
2215                 tmp->hide_fade_line();
2216         }
2217 }
2218
2219 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2220         : RegionDrag (e, i, p, v)
2221 {
2222         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2223 }
2224
2225 void
2226 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2227 {
2228         Drag::start_grab (event, cursor);
2229
2230         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2231         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2232
2233         _editor->show_verbose_duration_cursor (r->last_frame() - r->fade_out()->back()->when, r->last_frame(), 10);
2234         
2235         arv->show_fade_line(r->length() - r->fade_out()->back()->when);
2236 }
2237
2238 void
2239 FadeOutDrag::setup_pointer_frame_offset ()
2240 {
2241         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2242         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2243         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2244 }       
2245
2246 void
2247 FadeOutDrag::motion (GdkEvent* event, bool)
2248 {
2249         framecnt_t fade_length;
2250
2251         framepos_t const pos = adjusted_current_frame (event);
2252
2253         boost::shared_ptr<Region> region = _primary->region ();
2254
2255         if (pos > (region->last_frame() - 64)) {
2256                 fade_length = 64; // this should really be a minimum fade defined somewhere
2257         }
2258         else if (pos < region->position()) {
2259                 fade_length = region->length();
2260         }
2261         else {
2262                 fade_length = region->last_frame() - pos;
2263         }
2264
2265         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2266
2267                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2268
2269                 if (!tmp) {
2270                         continue;
2271                 }
2272
2273                 tmp->reset_fade_out_shape_width (fade_length);
2274                 tmp->show_fade_line(region->length() - fade_length);
2275         }
2276
2277         _editor->show_verbose_duration_cursor (region->last_frame() - fade_length, region->last_frame(), 10);
2278 }
2279
2280 void
2281 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2282 {
2283         if (!movement_occurred) {
2284                 return;
2285         }
2286
2287         framecnt_t fade_length;
2288
2289         framepos_t const pos = adjusted_current_frame (event);
2290
2291         boost::shared_ptr<Region> region = _primary->region ();
2292
2293         if (pos > (region->last_frame() - 64)) {
2294                 fade_length = 64; // this should really be a minimum fade defined somewhere
2295         }
2296         else if (pos < region->position()) {
2297                 fade_length = region->length();
2298         }
2299         else {
2300                 fade_length = region->last_frame() - pos;
2301         }
2302
2303         _editor->begin_reversible_command (_("change fade out length"));
2304
2305         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2306
2307                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2308
2309                 if (!tmp) {
2310                         continue;
2311                 }
2312
2313                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2314                 XMLNode &before = alist->get_state();
2315
2316                 tmp->audio_region()->set_fade_out_length (fade_length);
2317                 tmp->audio_region()->set_fade_out_active (true);
2318                 tmp->hide_fade_line();
2319
2320                 XMLNode &after = alist->get_state();
2321                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2322         }
2323
2324         _editor->commit_reversible_command ();
2325 }
2326
2327 void
2328 FadeOutDrag::aborted (bool)
2329 {
2330         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2331                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2332
2333                 if (!tmp) {
2334                         continue;
2335                 }
2336
2337                 tmp->reset_fade_out_shape_width (tmp->audio_region()->fade_out()->back()->when);
2338                 tmp->hide_fade_line();
2339         }
2340 }
2341
2342 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2343         : Drag (e, i)
2344 {
2345         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2346         
2347         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2348         assert (_marker);
2349
2350         _points.push_back (Gnome::Art::Point (0, 0));
2351         _points.push_back (Gnome::Art::Point (0, physical_screen_height (_editor->get_window())));
2352 }
2353
2354 MarkerDrag::~MarkerDrag ()
2355 {
2356         for (list<Location*>::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2357                 delete *i;
2358         }
2359 }
2360
2361 void
2362 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2363 {
2364         Drag::start_grab (event, cursor);
2365
2366         bool is_start;
2367
2368         Location *location = _editor->find_location_from_marker (_marker, is_start);
2369         _editor->_dragging_edit_point = true;
2370
2371         update_item (location);
2372
2373         // _drag_line->show();
2374         // _line->raise_to_top();
2375
2376         if (is_start) {
2377                 _editor->show_verbose_time_cursor (location->start(), 10);
2378         } else {
2379                 _editor->show_verbose_time_cursor (location->end(), 10);
2380         }
2381
2382         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2383
2384         switch (op) {
2385         case Selection::Toggle:
2386                 _editor->selection->toggle (_marker);
2387                 break;
2388         case Selection::Set:
2389                 if (!_editor->selection->selected (_marker)) {
2390                         _editor->selection->set (_marker);
2391                 }
2392                 break;
2393         case Selection::Extend:
2394         {
2395                 Locations::LocationList ll;
2396                 list<Marker*> to_add;
2397                 framepos_t s, e;
2398                 _editor->selection->markers.range (s, e);
2399                 s = min (_marker->position(), s);
2400                 e = max (_marker->position(), e);
2401                 s = min (s, e);
2402                 e = max (s, e);
2403                 if (e < max_framepos) {
2404                         ++e;
2405                 }
2406                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2407                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2408                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2409                         if (lm) {
2410                                 if (lm->start) {
2411                                         to_add.push_back (lm->start);
2412                                 }
2413                                 if (lm->end) {
2414                                         to_add.push_back (lm->end);
2415                                 }
2416                         }
2417                 }
2418                 if (!to_add.empty()) {
2419                         _editor->selection->add (to_add);
2420                 }
2421                 break;
2422         }
2423         case Selection::Add:
2424                 _editor->selection->add (_marker);
2425                 break;
2426         }
2427
2428         /* Set up copies for us to manipulate during the drag */
2429
2430         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2431                 Location* l = _editor->find_location_from_marker (*i, is_start);
2432                 _copied_locations.push_back (new Location (*l));
2433         }
2434 }
2435
2436 void
2437 MarkerDrag::setup_pointer_frame_offset ()
2438 {
2439         bool is_start;
2440         Location *location = _editor->find_location_from_marker (_marker, is_start);
2441         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2442 }
2443
2444 void
2445 MarkerDrag::motion (GdkEvent* event, bool)
2446 {
2447         framecnt_t f_delta = 0;
2448         bool is_start;
2449         bool move_both = false;
2450         Marker* marker;
2451         Location *real_location;
2452         Location *copy_location = 0;
2453
2454         framepos_t const newframe = adjusted_current_frame (event);
2455
2456         framepos_t next = newframe;
2457
2458         if (newframe == last_pointer_frame()) {
2459                 return;
2460         }
2461
2462         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2463                 move_both = true;
2464         }
2465
2466         MarkerSelection::iterator i;
2467         list<Location*>::iterator x;
2468
2469         /* find the marker we're dragging, and compute the delta */
2470
2471         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2472              x != _copied_locations.end() && i != _editor->selection->markers.end();
2473              ++i, ++x) {
2474
2475                 copy_location = *x;
2476                 marker = *i;
2477
2478                 if (marker == _marker) {
2479
2480                         if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2481                                 /* que pasa ?? */
2482                                 return;
2483                         }
2484
2485                         if (real_location->is_mark()) {
2486                                 f_delta = newframe - copy_location->start();
2487                         } else {
2488
2489
2490                                 switch (marker->type()) {
2491                                 case Marker::SessionStart:
2492                                 case Marker::RangeStart:
2493                                 case Marker::LoopStart:
2494                                 case Marker::PunchIn:
2495                                         f_delta = newframe - copy_location->start();
2496                                         break;
2497
2498                                 case Marker::SessionEnd:
2499                                 case Marker::RangeEnd:
2500                                 case Marker::LoopEnd:
2501                                 case Marker::PunchOut:
2502                                         f_delta = newframe - copy_location->end();
2503                                         break;
2504                                 default:
2505                                         /* what kind of marker is this ? */
2506                                         return;
2507                                 }
2508                         }
2509                         break;
2510                 }
2511         }
2512
2513         if (i == _editor->selection->markers.end()) {
2514                 /* hmm, impossible - we didn't find the dragged marker */
2515                 return;
2516         }
2517
2518         /* now move them all */
2519
2520         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2521              x != _copied_locations.end() && i != _editor->selection->markers.end();
2522              ++i, ++x) {
2523
2524                 copy_location = *x;
2525                 marker = *i;
2526
2527                 /* call this to find out if its the start or end */
2528
2529                 if ((real_location = _editor->find_location_from_marker (marker, is_start)) == 0) {
2530                         continue;
2531                 }
2532
2533                 if (real_location->locked()) {
2534                         continue;
2535                 }
2536
2537                 if (copy_location->is_mark()) {
2538
2539                         /* now move it */
2540
2541                         copy_location->set_start (copy_location->start() + f_delta);
2542
2543                 } else {
2544
2545                         framepos_t new_start = copy_location->start() + f_delta;
2546                         framepos_t new_end = copy_location->end() + f_delta;
2547
2548                         if (is_start) { // start-of-range marker
2549
2550                                 if (move_both) {
2551                                         copy_location->set_start (new_start);
2552                                         copy_location->set_end (new_end);
2553                                 } else  if (new_start < copy_location->end()) {
2554                                         copy_location->set_start (new_start);
2555                                 } else if (newframe > 0) {
2556                                         _editor->snap_to (next, 1, true);
2557                                         copy_location->set_end (next);
2558                                         copy_location->set_start (newframe);
2559                                 }
2560
2561                         } else { // end marker
2562
2563                                 if (move_both) {
2564                                         copy_location->set_end (new_end);
2565                                         copy_location->set_start (new_start);
2566                                 } else if (new_end > copy_location->start()) {
2567                                         copy_location->set_end (new_end);
2568                                 } else if (newframe > 0) {
2569                                         _editor->snap_to (next, -1, true);
2570                                         copy_location->set_start (next);
2571                                         copy_location->set_end (newframe);
2572                                 }
2573                         }
2574                 }
2575
2576                 update_item (copy_location);
2577
2578                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2579
2580                 if (lm) {
2581                         lm->set_position (copy_location->start(), copy_location->end());
2582                 }
2583         }
2584
2585         assert (!_copied_locations.empty());
2586
2587         _editor->show_verbose_time_cursor (newframe, 10);
2588
2589 #ifdef GTKOSX
2590         _editor->update_canvas_now ();
2591 #endif
2592 }
2593
2594 void
2595 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2596 {
2597         if (!movement_occurred) {
2598
2599                 /* just a click, do nothing but finish
2600                    off the selection process
2601                 */
2602
2603                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2604
2605                 switch (op) {
2606                 case Selection::Set:
2607                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
2608                                 _editor->selection->set (_marker);
2609                         }
2610                         break;
2611
2612                 case Selection::Toggle:
2613                 case Selection::Extend:
2614                 case Selection::Add:
2615                         break;
2616                 }
2617
2618                 return;
2619         }
2620
2621         _editor->_dragging_edit_point = false;
2622
2623         _editor->begin_reversible_command ( _("move marker") );
2624         XMLNode &before = _editor->session()->locations()->get_state();
2625
2626         MarkerSelection::iterator i;
2627         list<Location*>::iterator x;
2628         bool is_start;
2629
2630         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
2631              x != _copied_locations.end() && i != _editor->selection->markers.end();
2632              ++i, ++x) {
2633
2634                 Location * location = _editor->find_location_from_marker (*i, is_start);
2635
2636                 if (location) {
2637
2638                         if (location->locked()) {
2639                                 return;
2640                         }
2641
2642                         if (location->is_mark()) {
2643                                 location->set_start ((*x)->start());
2644                         } else {
2645                                 location->set ((*x)->start(), (*x)->end());
2646                         }
2647                 }
2648         }
2649
2650         XMLNode &after = _editor->session()->locations()->get_state();
2651         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
2652         _editor->commit_reversible_command ();
2653 }
2654
2655 void
2656 MarkerDrag::aborted (bool)
2657 {
2658         /* XXX: TODO */
2659 }
2660
2661 void
2662 MarkerDrag::update_item (Location* location)
2663 {
2664         /* noop */
2665 }
2666
2667 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
2668         : Drag (e, i),
2669           _cumulative_x_drag (0),
2670           _cumulative_y_drag (0)
2671 {
2672         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
2673         
2674         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
2675         assert (_point);
2676 }
2677
2678
2679 void
2680 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2681 {
2682         Drag::start_grab (event, _editor->cursors()->fader);
2683
2684         // start the grab at the center of the control point so
2685         // the point doesn't 'jump' to the mouse after the first drag
2686         _fixed_grab_x = _point->get_x();
2687         _fixed_grab_y = _point->get_y();
2688
2689         float const fraction = 1 - (_point->get_y() / _point->line().height());
2690
2691         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
2692
2693         _editor->set_verbose_canvas_cursor (_point->line().get_verbose_cursor_string (fraction),
2694                                             event->button.x + 10, event->button.y + 10);
2695
2696         _editor->show_verbose_canvas_cursor ();
2697 }
2698
2699 void
2700 ControlPointDrag::motion (GdkEvent* event, bool)
2701 {
2702         double dx = _drags->current_pointer_x() - last_pointer_x();
2703         double dy = _drags->current_pointer_y() - last_pointer_y();
2704
2705         if (event->button.state & Keyboard::SecondaryModifier) {
2706                 dx *= 0.1;
2707                 dy *= 0.1;
2708         }
2709
2710         /* coordinate in pixels relative to the start of the region (for region-based automation)
2711            or track (for track-based automation) */
2712         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
2713         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2714
2715         // calculate zero crossing point. back off by .01 to stay on the
2716         // positive side of zero
2717         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
2718
2719         // make sure we hit zero when passing through
2720         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
2721                 cy = zero_gain_y;
2722         }
2723
2724         if (_x_constrained) {
2725                 cx = _fixed_grab_x;
2726         }
2727         if (_y_constrained) {
2728                 cy = _fixed_grab_y;
2729         }
2730
2731         _cumulative_x_drag = cx - _fixed_grab_x;
2732         _cumulative_y_drag = cy - _fixed_grab_y;
2733
2734         cx = max (0.0, cx);
2735         cy = max (0.0, cy);
2736         cy = min ((double) _point->line().height(), cy);
2737
2738         framepos_t cx_frames = _editor->unit_to_frame (cx);
2739         
2740         if (!_x_constrained) {
2741                 _editor->snap_to_with_modifier (cx_frames, event);
2742         }
2743
2744         cx_frames = min (cx_frames, _point->line().maximum_time());
2745
2746         float const fraction = 1.0 - (cy / _point->line().height());
2747
2748         bool const push = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
2749
2750         _point->line().drag_motion (_editor->frame_to_unit (cx_frames), fraction, false, push);
2751
2752         _editor->set_verbose_canvas_cursor_text (_point->line().get_verbose_cursor_string (fraction));
2753 }
2754
2755 void
2756 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
2757 {
2758         if (!movement_occurred) {
2759
2760                 /* just a click */
2761
2762                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2763                         _editor->reset_point_selection ();
2764                 }
2765
2766         } else {
2767                 motion (event, false);
2768         }
2769         
2770         _point->line().end_drag ();
2771         _editor->session()->commit_reversible_command ();
2772 }
2773
2774 void
2775 ControlPointDrag::aborted (bool)
2776 {
2777         _point->line().reset ();
2778 }
2779
2780 bool
2781 ControlPointDrag::active (Editing::MouseMode m)
2782 {
2783         if (m == Editing::MouseGain) {
2784                 /* always active in mouse gain */
2785                 return true;
2786         }
2787
2788         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
2789         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
2790 }
2791
2792 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
2793         : Drag (e, i),
2794           _line (0),
2795           _cumulative_y_drag (0)
2796 {
2797         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
2798 }
2799
2800 void
2801 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2802 {
2803         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
2804         assert (_line);
2805
2806         _item = &_line->grab_item ();
2807
2808         /* need to get x coordinate in terms of parent (TimeAxisItemView)
2809            origin, and ditto for y.
2810         */
2811
2812         double cx = event->button.x;
2813         double cy = event->button.y;
2814
2815         _line->parent_group().w2i (cx, cy);
2816
2817         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->frames_per_unit);
2818
2819         uint32_t before;
2820         uint32_t after;
2821         
2822         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
2823                 /* no adjacent points */
2824                 return;
2825         }
2826
2827         Drag::start_grab (event, _editor->cursors()->fader);
2828
2829         /* store grab start in parent frame */
2830
2831         _fixed_grab_x = cx;
2832         _fixed_grab_y = cy;
2833
2834         double fraction = 1.0 - (cy / _line->height());
2835
2836         _line->start_drag_line (before, after, fraction);
2837
2838         _editor->set_verbose_canvas_cursor (_line->get_verbose_cursor_string (fraction),
2839                                             event->button.x + 10, event->button.y + 10);
2840
2841         _editor->show_verbose_canvas_cursor ();
2842 }
2843
2844 void
2845 LineDrag::motion (GdkEvent* event, bool)
2846 {
2847         double dy = _drags->current_pointer_y() - last_pointer_y();
2848
2849         if (event->button.state & Keyboard::SecondaryModifier) {
2850                 dy *= 0.1;
2851         }
2852
2853         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
2854
2855         _cumulative_y_drag = cy - _fixed_grab_y;
2856
2857         cy = max (0.0, cy);
2858         cy = min ((double) _line->height(), cy);
2859
2860         double const fraction = 1.0 - (cy / _line->height());
2861
2862         bool push;
2863
2864         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier)) {
2865                 push = false;
2866         } else {
2867                 push = true;
2868         }
2869
2870         /* we are ignoring x position for this drag, so we can just pass in anything */
2871         _line->drag_motion (0, fraction, true, push);
2872
2873         _editor->set_verbose_canvas_cursor_text (_line->get_verbose_cursor_string (fraction));
2874 }
2875
2876 void
2877 LineDrag::finished (GdkEvent* event, bool)
2878 {
2879         motion (event, false);
2880         _line->end_drag ();
2881         _editor->session()->commit_reversible_command ();
2882 }
2883
2884 void
2885 LineDrag::aborted (bool)
2886 {
2887         _line->reset ();
2888 }
2889
2890 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
2891         : Drag (e, i),
2892           _line (0),
2893           _cumulative_x_drag (0)
2894 {
2895         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
2896 }
2897
2898 void
2899 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
2900 {
2901         Drag::start_grab (event);
2902         
2903         _line = reinterpret_cast<SimpleLine*> (_item);
2904         assert (_line);
2905
2906         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
2907
2908         double cx = event->button.x;
2909         double cy = event->button.y;
2910
2911         _item->property_parent().get_value()->w2i(cx, cy);
2912
2913         /* store grab start in parent frame */
2914         _region_view_grab_x = cx;
2915         
2916         _before = _line->property_x1();
2917         
2918         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2919                 
2920         _max_x = _editor->frame_to_pixel(_arv->get_duration());
2921 }
2922
2923 void
2924 FeatureLineDrag::motion (GdkEvent*, bool)
2925 {
2926         double dx = _drags->current_pointer_x() - last_pointer_x();
2927         
2928         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
2929         
2930         _cumulative_x_drag += dx;
2931                 
2932         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
2933         
2934         if (cx > _max_x){
2935                 cx = _max_x;
2936         }
2937         else if(cx < 0){
2938                 cx = 0;
2939         }
2940         
2941         _line->property_x1() = cx; 
2942         _line->property_x2() = cx;
2943
2944         _before = _line->property_x1();
2945 }
2946
2947 void
2948 FeatureLineDrag::finished (GdkEvent*, bool)
2949 {
2950         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
2951         _arv->update_transient(_before, _line->property_x1());
2952 }
2953
2954 void
2955 FeatureLineDrag::aborted (bool)
2956 {
2957         //_line->reset ();
2958 }
2959
2960 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
2961         : Drag (e, i)
2962 {
2963         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
2964 }
2965
2966 void
2967 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
2968 {
2969         Drag::start_grab (event);
2970         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
2971 }
2972
2973 void
2974 RubberbandSelectDrag::motion (GdkEvent* event, bool)
2975 {
2976         framepos_t start;
2977         framepos_t end;
2978         double y1;
2979         double y2;
2980
2981         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
2982
2983         framepos_t grab = grab_frame ();
2984         if (Config->get_rubberbanding_snaps_to_grid ()) {
2985                 _editor->snap_to_with_modifier (grab, event);
2986         }
2987
2988         /* base start and end on initial click position */
2989
2990         if (pf < grab) {
2991                 start = pf;
2992                 end = grab;
2993         } else {
2994                 end = pf;
2995                 start = grab;
2996         }
2997
2998         if (_drags->current_pointer_y() < grab_y()) {
2999                 y1 = _drags->current_pointer_y();
3000                 y2 = grab_y();
3001         } else {
3002                 y2 = _drags->current_pointer_y();
3003                 y1 = grab_y();
3004         }
3005
3006
3007         if (start != end || y1 != y2) {
3008
3009                 double x1 = _editor->frame_to_pixel (start);
3010                 double x2 = _editor->frame_to_pixel (end);
3011
3012                 _editor->rubberband_rect->property_x1() = x1;
3013                 _editor->rubberband_rect->property_y1() = y1;
3014                 _editor->rubberband_rect->property_x2() = x2;
3015                 _editor->rubberband_rect->property_y2() = y2;
3016
3017                 _editor->rubberband_rect->show();
3018                 _editor->rubberband_rect->raise_to_top();
3019
3020                 _editor->show_verbose_time_cursor (pf, 10);
3021         }
3022 }
3023
3024 void
3025 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3026 {
3027         if (movement_occurred) {
3028
3029                 motion (event, false);
3030
3031                 double y1,y2;
3032                 if (_drags->current_pointer_y() < grab_y()) {
3033                         y1 = _drags->current_pointer_y();
3034                         y2 = grab_y();
3035                 } else {
3036                         y2 = _drags->current_pointer_y();
3037                         y1 = grab_y();
3038                 }
3039
3040
3041                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3042                 bool committed;
3043
3044                 _editor->begin_reversible_command (_("rubberband selection"));
3045
3046                 if (grab_frame() < last_pointer_frame()) {
3047                         _editor->select_all_within (grab_frame(), last_pointer_frame() - 1, y1, y2, _editor->track_views, op, false);
3048                 } else {
3049                         _editor->select_all_within (last_pointer_frame(), grab_frame() - 1, y1, y2, _editor->track_views, op, false);
3050                 }
3051
3052                 _editor->commit_reversible_command ();
3053         } else {
3054                 if (!getenv("ARDOUR_SAE")) {
3055                         _editor->selection->clear_tracks();
3056                 }
3057                 _editor->selection->clear_regions();
3058                 _editor->selection->clear_points ();
3059                 _editor->selection->clear_lines ();
3060         }
3061
3062         _editor->rubberband_rect->hide();
3063 }
3064
3065 void
3066 RubberbandSelectDrag::aborted (bool)
3067 {
3068         _editor->rubberband_rect->hide ();
3069 }
3070
3071 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3072         : RegionDrag (e, i, p, v)
3073 {
3074         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3075 }
3076
3077 void
3078 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3079 {
3080         Drag::start_grab (event, cursor);
3081
3082         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3083 }
3084
3085 void
3086 TimeFXDrag::motion (GdkEvent* event, bool)
3087 {
3088         RegionView* rv = _primary;
3089
3090         framepos_t const pf = adjusted_current_frame (event);
3091
3092         if (pf > rv->region()->position()) {
3093                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf);
3094         }
3095
3096         _editor->show_verbose_time_cursor (pf, 10);
3097 }
3098
3099 void
3100 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3101 {
3102         _primary->get_time_axis_view().hide_timestretch ();
3103
3104         if (!movement_occurred) {
3105                 return;
3106         }
3107
3108         if (last_pointer_frame() < _primary->region()->position()) {
3109                 /* backwards drag of the left edge - not usable */
3110                 return;
3111         }
3112
3113         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3114
3115         float percentage = (double) newlen / (double) _primary->region()->length();
3116
3117 #ifndef USE_RUBBERBAND
3118         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3119         if (_primary->region()->data_type() == DataType::AUDIO) {
3120                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3121         }
3122 #endif
3123
3124         _editor->begin_reversible_command (_("timestretch"));
3125
3126         // XXX how do timeFX on multiple regions ?
3127
3128         RegionSelection rs;
3129         rs.add (_primary);
3130
3131         if (_editor->time_stretch (rs, percentage) == -1) {
3132                 error << _("An error occurred while executing time stretch operation") << endmsg;
3133         }
3134 }
3135
3136 void
3137 TimeFXDrag::aborted (bool)
3138 {
3139         _primary->get_time_axis_view().hide_timestretch ();
3140 }
3141
3142 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3143         : Drag (e, i)
3144 {
3145         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3146 }
3147
3148 void
3149 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3150 {
3151         Drag::start_grab (event);
3152 }
3153
3154 void
3155 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3156 {
3157         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3158 }
3159
3160 void
3161 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3162 {
3163         if (movement_occurred && _editor->session()) {
3164                 /* make sure we stop */
3165                 _editor->session()->request_transport_speed (0.0);
3166         }
3167 }
3168
3169 void
3170 ScrubDrag::aborted (bool)
3171 {
3172         /* XXX: TODO */
3173 }
3174
3175 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3176         : Drag (e, i)
3177         , _operation (o)
3178         , _copy (false)
3179         , _original_pointer_time_axis (-1)
3180         , _last_pointer_time_axis (-1)
3181 {
3182         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3183 }
3184
3185 void
3186 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3187 {
3188         if (_editor->session() == 0) {
3189                 return;
3190         }
3191
3192         Gdk::Cursor* cursor = 0;
3193
3194         switch (_operation) {
3195         case CreateSelection:
3196                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3197                         _copy = true;
3198                 } else {
3199                         _copy = false;
3200                 }
3201                 cursor = _editor->cursors()->selector;
3202                 Drag::start_grab (event, cursor);
3203                 break;
3204
3205         case SelectionStartTrim:
3206                 if (_editor->clicked_axisview) {
3207                         _editor->clicked_axisview->order_selection_trims (_item, true);
3208                 }
3209                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3210                 break;
3211
3212         case SelectionEndTrim:
3213                 if (_editor->clicked_axisview) {
3214                         _editor->clicked_axisview->order_selection_trims (_item, false);
3215                 }
3216                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3217                 break;
3218
3219         case SelectionMove:
3220                 Drag::start_grab (event, cursor);
3221                 break;
3222         }
3223
3224         if (_operation == SelectionMove) {
3225                 _editor->show_verbose_time_cursor (_editor->selection->time[_editor->clicked_selection].start, 10);
3226         } else {
3227                 _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3228         }
3229
3230         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3231 }
3232
3233 void
3234 SelectionDrag::setup_pointer_frame_offset ()
3235 {
3236         switch (_operation) {
3237         case CreateSelection:
3238                 _pointer_frame_offset = 0;
3239                 break;
3240
3241         case SelectionStartTrim:
3242         case SelectionMove:
3243                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3244                 break;
3245
3246         case SelectionEndTrim:
3247                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3248                 break;
3249         }
3250 }
3251
3252 void
3253 SelectionDrag::motion (GdkEvent* event, bool first_move)
3254 {
3255         framepos_t start = 0;
3256         framepos_t end = 0;
3257         framecnt_t length;
3258
3259         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3260         if (pending_time_axis.first == 0) {
3261                 return;
3262         }
3263         
3264         framepos_t const pending_position = adjusted_current_frame (event);
3265
3266         /* only alter selection if things have changed */
3267
3268         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3269                 return;
3270         }
3271
3272         switch (_operation) {
3273         case CreateSelection:
3274         {
3275                 framepos_t grab = grab_frame ();
3276
3277                 if (first_move) {
3278                         _editor->snap_to (grab);
3279                 }
3280
3281                 if (pending_position < grab_frame()) {
3282                         start = pending_position;
3283                         end = grab;
3284                 } else {
3285                         end = pending_position;
3286                         start = grab;
3287                 }
3288
3289                 /* first drag: Either add to the selection
3290                    or create a new selection
3291                 */
3292
3293                 if (first_move) {
3294
3295                         if (_copy) {
3296                                 /* adding to the selection */
3297                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3298                                 //_editor->selection->add (_editor->clicked_axisview);
3299                                 _editor->clicked_selection = _editor->selection->add (start, end);
3300                                 _copy = false;
3301                         } else {
3302                                 /* new selection */
3303
3304                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3305                                         //_editor->selection->set (_editor->clicked_axisview);
3306                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3307                                 }
3308                                 
3309                                 _editor->clicked_selection = _editor->selection->set (start, end);
3310                         }
3311                 }
3312
3313                 /* select the track that we're in */
3314                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3315                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3316                         _editor->selection->add (pending_time_axis.first);
3317                         _added_time_axes.push_back (pending_time_axis.first);
3318                 }
3319
3320                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3321                    tracks that we selected in the first place.
3322                 */
3323                 
3324                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3325                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3326
3327                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3328                 while (i != _added_time_axes.end()) {
3329
3330                         list<TimeAxisView*>::iterator tmp = i;
3331                         ++tmp;
3332                         
3333                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3334                                 _editor->selection->remove (*i);
3335                                 _added_time_axes.remove (*i);
3336                         }
3337
3338                         i = tmp;
3339                 }
3340
3341         }
3342         break;
3343
3344         case SelectionStartTrim:
3345
3346                 start = _editor->selection->time[_editor->clicked_selection].start;
3347                 end = _editor->selection->time[_editor->clicked_selection].end;
3348
3349                 if (pending_position > end) {
3350                         start = end;
3351                 } else {
3352                         start = pending_position;
3353                 }
3354                 break;
3355
3356         case SelectionEndTrim:
3357
3358                 start = _editor->selection->time[_editor->clicked_selection].start;
3359                 end = _editor->selection->time[_editor->clicked_selection].end;
3360
3361                 if (pending_position < start) {
3362                         end = start;
3363                 } else {
3364                         end = pending_position;
3365                 }
3366
3367                 break;
3368
3369         case SelectionMove:
3370
3371                 start = _editor->selection->time[_editor->clicked_selection].start;
3372                 end = _editor->selection->time[_editor->clicked_selection].end;
3373
3374                 length = end - start;
3375
3376                 start = pending_position;
3377                 _editor->snap_to (start);
3378
3379                 end = start + length;
3380
3381                 break;
3382         }
3383
3384         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3385                 _editor->start_canvas_autoscroll (1, 0);
3386         }
3387
3388         if (start != end) {
3389                 _editor->selection->replace (_editor->clicked_selection, start, end);
3390         }
3391
3392         if (_operation == SelectionMove) {
3393                 _editor->show_verbose_time_cursor(start, 10);
3394         } else {
3395                 _editor->show_verbose_time_cursor(pending_position, 10);
3396         }
3397 }
3398
3399 void
3400 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3401 {
3402         Session* s = _editor->session();
3403
3404         if (movement_occurred) {
3405                 motion (event, false);
3406                 /* XXX this is not object-oriented programming at all. ick */
3407                 if (_editor->selection->time.consolidate()) {
3408                         _editor->selection->TimeChanged ();
3409                 }
3410
3411                 /* XXX what if its a music time selection? */
3412                 if (s && (s->config.get_auto_play() || (s->get_play_range() && s->transport_rolling()))) {
3413                         s->request_play_range (&_editor->selection->time, true);
3414                 }
3415
3416
3417         } else {
3418                 /* just a click, no pointer movement.*/
3419
3420                 if (Keyboard::no_modifier_keys_pressed (&event->button)) {
3421                         _editor->selection->clear_time();
3422                 }
3423
3424                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3425                         _editor->selection->set (_editor->clicked_axisview);
3426                 }
3427                 
3428                 if (s && s->get_play_range () && s->transport_rolling()) {
3429                         s->request_stop (false, false);
3430                 }
3431
3432         }
3433
3434         _editor->stop_canvas_autoscroll ();
3435 }
3436
3437 void
3438 SelectionDrag::aborted (bool)
3439 {
3440         /* XXX: TODO */
3441 }
3442
3443 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3444         : Drag (e, i),
3445           _operation (o),
3446           _copy (false)
3447 {
3448         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3449         
3450         _drag_rect = new ArdourCanvas::SimpleRect (*_editor->time_line_group, 0.0, 0.0, 0.0, 
3451                                                    physical_screen_height (_editor->get_window()));
3452         _drag_rect->hide ();
3453
3454         _drag_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3455         _drag_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RangeDragRect.get();
3456 }
3457
3458 void
3459 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3460 {
3461         if (_editor->session() == 0) {
3462                 return;
3463         }
3464
3465         Gdk::Cursor* cursor = 0;
3466
3467         if (!_editor->temp_location) {
3468                 _editor->temp_location = new Location (*_editor->session());
3469         }
3470
3471         switch (_operation) {
3472         case CreateRangeMarker:
3473         case CreateTransportMarker:
3474         case CreateCDMarker:
3475
3476                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3477                         _copy = true;
3478                 } else {
3479                         _copy = false;
3480                 }
3481                 cursor = _editor->cursors()->selector;
3482                 break;
3483         }
3484
3485         Drag::start_grab (event, cursor);
3486
3487         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3488 }
3489
3490 void
3491 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
3492 {
3493         framepos_t start = 0;
3494         framepos_t end = 0;
3495         ArdourCanvas::SimpleRect *crect;
3496
3497         switch (_operation) {
3498         case CreateRangeMarker:
3499                 crect = _editor->range_bar_drag_rect;
3500                 break;
3501         case CreateTransportMarker:
3502                 crect = _editor->transport_bar_drag_rect;
3503                 break;
3504         case CreateCDMarker:
3505                 crect = _editor->cd_marker_bar_drag_rect;
3506                 break;
3507         default:
3508                 cerr << "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()" << endl;
3509                 return;
3510                 break;
3511         }
3512
3513         framepos_t const pf = adjusted_current_frame (event);
3514
3515         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
3516                 framepos_t grab = grab_frame ();
3517                 _editor->snap_to (grab);
3518                 
3519                 if (pf < grab_frame()) {
3520                         start = pf;
3521                         end = grab;
3522                 } else {
3523                         end = pf;
3524                         start = grab;
3525                 }
3526
3527                 /* first drag: Either add to the selection
3528                    or create a new selection.
3529                 */
3530
3531                 if (first_move) {
3532
3533                         _editor->temp_location->set (start, end);
3534
3535                         crect->show ();
3536
3537                         update_item (_editor->temp_location);
3538                         _drag_rect->show();
3539                         //_drag_rect->raise_to_top();
3540
3541                 }
3542         }
3543
3544         if (event->button.x >= _editor->horizontal_position() + _editor->_canvas_width) {
3545                 _editor->start_canvas_autoscroll (1, 0);
3546         }
3547
3548         if (start != end) {
3549                 _editor->temp_location->set (start, end);
3550
3551                 double x1 = _editor->frame_to_pixel (start);
3552                 double x2 = _editor->frame_to_pixel (end);
3553                 crect->property_x1() = x1;
3554                 crect->property_x2() = x2;
3555
3556                 update_item (_editor->temp_location);
3557         }
3558
3559         _editor->show_verbose_time_cursor (pf, 10);
3560
3561 }
3562
3563 void
3564 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
3565 {
3566         Location * newloc = 0;
3567         string rangename;
3568         int flags;
3569
3570         if (movement_occurred) {
3571                 motion (event, false);
3572                 _drag_rect->hide();
3573
3574                 switch (_operation) {
3575                 case CreateRangeMarker:
3576                 case CreateCDMarker:
3577                     {
3578                         _editor->begin_reversible_command (_("new range marker"));
3579                         XMLNode &before = _editor->session()->locations()->get_state();
3580                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
3581                         if (_operation == CreateCDMarker) {
3582                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
3583                                 _editor->cd_marker_bar_drag_rect->hide();
3584                         }
3585                         else {
3586                                 flags = Location::IsRangeMarker;
3587                                 _editor->range_bar_drag_rect->hide();
3588                         }
3589                         newloc = new Location (
3590                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
3591                                 );
3592                         
3593                         _editor->session()->locations()->add (newloc, true);
3594                         XMLNode &after = _editor->session()->locations()->get_state();
3595                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3596                         _editor->commit_reversible_command ();
3597                         break;
3598                     }
3599
3600                 case CreateTransportMarker:
3601                         // popup menu to pick loop or punch
3602                         _editor->new_transport_marker_context_menu (&event->button, _item);
3603                         break;
3604                 }
3605         } else {
3606                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
3607
3608                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
3609
3610                         framepos_t start;
3611                         framepos_t end;
3612
3613                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
3614
3615                         if (end == max_framepos) {
3616                                 end = _editor->session()->current_end_frame ();
3617                         }
3618
3619                         if (start == max_framepos) {
3620                                 start = _editor->session()->current_start_frame ();
3621                         }
3622
3623                         switch (_editor->mouse_mode) {
3624                         case MouseObject:
3625                                 /* find the two markers on either side and then make the selection from it */
3626                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
3627                                 break;
3628
3629                         case MouseRange:
3630                                 /* find the two markers on either side of the click and make the range out of it */
3631                                 _editor->selection->set (start, end);
3632                                 break;
3633
3634                         default:
3635                                 break;
3636                         }
3637                 }
3638         }
3639
3640         _editor->stop_canvas_autoscroll ();
3641 }
3642
3643 void
3644 RangeMarkerBarDrag::aborted (bool)
3645 {
3646         /* XXX: TODO */
3647 }
3648
3649 void
3650 RangeMarkerBarDrag::update_item (Location* location)
3651 {
3652         double const x1 = _editor->frame_to_pixel (location->start());
3653         double const x2 = _editor->frame_to_pixel (location->end());
3654
3655         _drag_rect->property_x1() = x1;
3656         _drag_rect->property_x2() = x2;
3657 }
3658
3659 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
3660         : Drag (e, i)
3661         , _zoom_out (false)
3662 {
3663         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
3664 }
3665
3666 void
3667 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3668 {
3669         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
3670                 Drag::start_grab (event, _editor->cursors()->zoom_out);
3671                 _zoom_out = true;
3672         } else {
3673                 Drag::start_grab (event, _editor->cursors()->zoom_in);
3674                 _zoom_out = false;
3675         }
3676                 
3677         _editor->show_verbose_time_cursor (adjusted_current_frame (event), 10);
3678 }
3679
3680 void
3681 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
3682 {
3683         framepos_t start;
3684         framepos_t end;
3685
3686         framepos_t const pf = adjusted_current_frame (event);
3687
3688         framepos_t grab = grab_frame ();
3689         _editor->snap_to_with_modifier (grab, event);
3690
3691         /* base start and end on initial click position */
3692         if (pf < grab) {
3693                 start = pf;
3694                 end = grab;
3695         } else {
3696                 end = pf;
3697                 start = grab;
3698         }
3699
3700         if (start != end) {
3701
3702                 if (first_move) {
3703                         _editor->zoom_rect->show();
3704                         _editor->zoom_rect->raise_to_top();
3705                 }
3706
3707                 _editor->reposition_zoom_rect(start, end);
3708
3709                 _editor->show_verbose_time_cursor (pf, 10);
3710         }
3711 }
3712
3713 void
3714 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
3715 {
3716         if (movement_occurred) {
3717                 motion (event, false);
3718
3719                 if (grab_frame() < last_pointer_frame()) {
3720                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame(), "mouse zoom");
3721                 } else {
3722                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame(), "mouse zoom");
3723                 }
3724         } else {
3725                 _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
3726         }
3727
3728         _editor->zoom_rect->hide();
3729 }
3730
3731 void
3732 MouseZoomDrag::aborted (bool)
3733 {
3734         _editor->zoom_rect->hide ();
3735 }
3736
3737 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
3738         : Drag (e, i)
3739         , _cumulative_dx (0)
3740         , _cumulative_dy (0)
3741 {
3742         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
3743
3744         _primary = dynamic_cast<CanvasNoteEvent*> (_item);
3745         _region = &_primary->region_view ();
3746         _note_height = _region->midi_stream_view()->note_height ();
3747 }
3748
3749 void
3750 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3751 {
3752         Drag::start_grab (event);
3753
3754         if (!(_was_selected = _primary->selected())) {
3755
3756                 /* tertiary-click means extend selection - we'll do that on button release,
3757                    so don't add it here, because otherwise we make it hard to figure
3758                    out the "extend-to" range.
3759                 */
3760
3761                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
3762
3763                 if (!extend) {
3764                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
3765
3766                         if (add) {
3767                                 _region->note_selected (_primary, true);
3768                         } else {
3769                                 _region->unique_select (_primary);
3770                         }
3771                 }
3772         }
3773 }
3774
3775 /** @return Current total drag x change in frames */
3776 frameoffset_t
3777 NoteDrag::total_dx () const
3778 {
3779         /* dx in frames */
3780         frameoffset_t const dx = _editor->unit_to_frame (_drags->current_pointer_x() - grab_x());
3781
3782         /* primary note time */
3783         frameoffset_t const n = _region->beats_to_frames (_primary->note()->time ());
3784         
3785         /* new time of the primary note relative to the region position */
3786         frameoffset_t const st = n + dx;
3787
3788         /* snap and return corresponding delta */
3789         return _region->snap_frame_to_frame (st) - n;
3790 }
3791
3792 /** @return Current total drag y change in notes */
3793 int8_t
3794 NoteDrag::total_dy () const
3795 {
3796         /* this is `backwards' to make increasing note number go in the right direction */
3797         double const dy = _drags->current_pointer_y() - grab_y();
3798
3799         /* dy in notes */
3800         int8_t ndy = 0;
3801
3802         if (abs (dy) >= _note_height) {
3803                 if (dy > 0) {
3804                         ndy = (int8_t) ceil (dy / _note_height / 2.0);
3805                 } else {
3806                         ndy = (int8_t) floor (dy / _note_height / 2.0);
3807                 }
3808         }
3809
3810         /* more positive value = higher pitch and higher y-axis position on track,
3811            which is the inverse of the X-centric geometric universe 
3812         */
3813
3814         return -ndy; 
3815 }       
3816
3817 void
3818 NoteDrag::motion (GdkEvent *, bool)
3819 {
3820         /* Total change in x and y since the start of the drag */
3821         frameoffset_t const dx = total_dx ();
3822         int8_t const dy = -total_dy ();
3823
3824         /* Now work out what we have to do to the note canvas items to set this new drag delta */
3825         double const tdx = _editor->frame_to_unit (dx) - _cumulative_dx;
3826         double const tdy = dy * _note_height - _cumulative_dy;
3827
3828         if (tdx || tdy) {
3829                 _cumulative_dx += tdx;
3830                 _cumulative_dy += tdy;
3831
3832                 int8_t note_delta = total_dy();
3833
3834                 _region->move_selection (tdx, tdy, note_delta);
3835
3836                 char buf[12];
3837                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (_primary->note()->note() + note_delta).c_str(),
3838                           (int) floor (_primary->note()->note() + note_delta));
3839                 
3840                 _editor->show_verbose_canvas_cursor_with (buf);
3841         }
3842 }
3843
3844 void
3845 NoteDrag::finished (GdkEvent* ev, bool moved)
3846 {
3847         if (!moved) {
3848                 if (_editor->current_mouse_mode() == Editing::MouseObject) {
3849
3850                         if (_was_selected) {
3851                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3852                                 if (add) {
3853                                         _region->note_deselected (_primary);
3854                                 }
3855                         } else {
3856                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
3857                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
3858
3859                                 if (!extend && !add && _region->selection_size() > 1) {
3860                                         _region->unique_select (_primary);
3861                                 } else if (extend) {
3862                                         _region->note_selected (_primary, true, true);
3863                                 } else {
3864                                         /* it was added during button press */
3865                                 }
3866                         }
3867                 }
3868         } else {
3869                 _region->note_dropped (_primary, total_dx(), total_dy());
3870         }
3871 }
3872
3873 void
3874 NoteDrag::aborted (bool)
3875 {
3876         /* XXX: TODO */
3877 }
3878
3879 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, ArdourCanvas::Item* item, list<AudioRange> const & r)
3880         : Drag (editor, item)
3881         , _ranges (r)
3882         , _nothing_to_drag (false)
3883 {
3884         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
3885         
3886         _atav = reinterpret_cast<AutomationTimeAxisView*> (_item->get_data ("trackview"));
3887         assert (_atav);
3888
3889         /* get all lines in the automation view */
3890         list<boost::shared_ptr<AutomationLine> > lines = _atav->lines ();
3891
3892         /* find those that overlap the ranges being dragged */
3893         list<boost::shared_ptr<AutomationLine> >::iterator i = lines.begin ();
3894         while (i != lines.end ()) {
3895                 list<boost::shared_ptr<AutomationLine> >::iterator j = i;
3896                 ++j;
3897
3898                 pair<framepos_t, framepos_t> const r = (*i)->get_point_x_range ();
3899
3900                 /* check this range against all the AudioRanges that we are using */
3901                 list<AudioRange>::const_iterator k = _ranges.begin ();
3902                 while (k != _ranges.end()) {
3903                         if (k->coverage (r.first, r.second) != OverlapNone) {
3904                                 break;
3905                         }
3906                         ++k;
3907                 }
3908
3909                 /* add it to our list if it overlaps at all */
3910                 if (k != _ranges.end()) {
3911                         Line n;
3912                         n.line = *i;
3913                         n.state = 0;
3914                         n.range = r;
3915                         _lines.push_back (n);
3916                 }
3917
3918                 i = j;
3919         }
3920
3921         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
3922 }
3923
3924 void
3925 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3926 {
3927         Drag::start_grab (event, cursor);
3928
3929         /* Get line states before we start changing things */
3930         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3931                 i->state = &i->line->get_state ();
3932         }
3933
3934         if (_ranges.empty()) {
3935
3936                 /* No selected time ranges: drag all points */
3937                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
3938                         uint32_t const N = i->line->npoints ();
3939                         for (uint32_t j = 0; j < N; ++j) {
3940                                 i->points.push_back (i->line->nth (j));
3941                         }
3942                 }
3943                 
3944         } else {
3945
3946                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
3947
3948                         framecnt_t const half = (i->start + i->end) / 2;
3949                         
3950                         /* find the line that this audio range starts in */
3951                         list<Line>::iterator j = _lines.begin();
3952                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
3953                                 ++j;
3954                         }
3955
3956                         if (j != _lines.end()) {
3957                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3958                                 
3959                                 /* j is the line that this audio range starts in; fade into it;
3960                                    64 samples length plucked out of thin air.
3961                                 */
3962
3963                                 framepos_t a = i->start + 64;
3964                                 if (a > half) {
3965                                         a = half;
3966                                 }
3967
3968                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
3969                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
3970
3971                                 the_list->add (p, the_list->eval (p));
3972                                 j->line->add_always_in_view (p);
3973                                 the_list->add (q, the_list->eval (q));
3974                                 j->line->add_always_in_view (q);
3975                         }
3976
3977                         /* same thing for the end */
3978                         
3979                         j = _lines.begin();
3980                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
3981                                 ++j;
3982                         }
3983
3984                         if (j != _lines.end()) {
3985                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
3986                                 
3987                                 /* j is the line that this audio range starts in; fade out of it;
3988                                    64 samples length plucked out of thin air.
3989                                 */
3990                                 
3991                                 framepos_t b = i->end - 64;
3992                                 if (b < half) {
3993                                         b = half;
3994                                 }
3995
3996                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
3997                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
3998                                 
3999                                 the_list->add (p, the_list->eval (p));
4000                                 j->line->add_always_in_view (p);
4001                                 the_list->add (q, the_list->eval (q));
4002                                 j->line->add_always_in_view (q);
4003                         }
4004                 }
4005
4006                 _nothing_to_drag = true;
4007
4008                 /* Find all the points that should be dragged and put them in the relevant
4009                    points lists in the Line structs.
4010                 */
4011
4012                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4013
4014                         uint32_t const N = i->line->npoints ();
4015                         for (uint32_t j = 0; j < N; ++j) {
4016
4017                                 /* here's a control point on this line */
4018                                 ControlPoint* p = i->line->nth (j);
4019                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4020
4021                                 /* see if it's inside a range */
4022                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4023                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4024                                         ++k;
4025                                 }
4026
4027                                 if (k != _ranges.end()) {
4028                                         /* dragging this point */
4029                                         _nothing_to_drag = false;
4030                                         i->points.push_back (p);
4031                                 }
4032                         }
4033                 }
4034         }
4035
4036         if (_nothing_to_drag) {
4037                 return;
4038         }
4039
4040         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4041                 i->line->start_drag_multiple (i->points, 1 - (_drags->current_pointer_y() / i->line->height ()), i->state);
4042         }
4043 }
4044
4045 void
4046 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4047 {
4048         if (_nothing_to_drag) {
4049                 return;
4050         }
4051
4052         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4053                 float const f = 1 - (_drags->current_pointer_y() / i->line->height());
4054
4055                 /* we are ignoring x position for this drag, so we can just pass in anything */
4056                 i->line->drag_motion (0, f, true, false);
4057         }
4058 }
4059
4060 void
4061 AutomationRangeDrag::finished (GdkEvent* event, bool)
4062 {
4063         if (_nothing_to_drag) {
4064                 return;
4065         }
4066         
4067         motion (event, false);
4068         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4069                 i->line->end_drag ();
4070                 i->line->clear_always_in_view ();
4071         }
4072
4073         _editor->session()->commit_reversible_command ();
4074 }
4075
4076 void
4077 AutomationRangeDrag::aborted (bool)
4078 {
4079         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4080                 i->line->clear_always_in_view ();
4081                 i->line->reset ();
4082         }
4083 }
4084
4085 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4086         : view (v)
4087 {
4088         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4089         layer = v->region()->layer ();
4090         initial_y = v->get_canvas_group()->property_y ();
4091         initial_playlist = v->region()->playlist ();
4092         initial_position = v->region()->position ();
4093         initial_end = v->region()->position () + v->region()->length ();
4094 }
4095
4096 PatchChangeDrag::PatchChangeDrag (Editor* e, CanvasPatchChange* i, MidiRegionView* r)
4097         : Drag (e, i)
4098         , _region_view (r)
4099         , _patch_change (i)
4100         , _cumulative_dx (0)
4101 {
4102         DEBUG_TRACE (DEBUG::Drags, "New PatchChangeDrag\n");
4103 }
4104
4105 void
4106 PatchChangeDrag::motion (GdkEvent* ev, bool)
4107 {
4108         framepos_t f = adjusted_current_frame (ev);
4109         boost::shared_ptr<Region> r = _region_view->region ();
4110         f = max (f, r->position ());
4111         f = min (f, r->last_frame ());
4112         
4113         framecnt_t const dxf = f - grab_frame();
4114         double const dxu = _editor->frame_to_unit (dxf);
4115         _patch_change->move (dxu - _cumulative_dx, 0);
4116         _cumulative_dx = dxu;
4117 }
4118
4119 void
4120 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4121 {
4122         if (!movement_occurred) {
4123                 return;
4124         }
4125
4126         boost::shared_ptr<Region> r (_region_view->region ());
4127         
4128         framepos_t f = adjusted_current_frame (ev);
4129         f = max (f, r->position ());
4130         f = min (f, r->last_frame ());
4131         
4132         _region_view->move_patch_change (
4133                 *_patch_change,
4134                 _region_view->frames_to_beats (f - r->position() - r->start())
4135                 );
4136 }
4137
4138 void
4139 PatchChangeDrag::aborted (bool)
4140 {
4141         _patch_change->move (-_cumulative_dx, 0);
4142 }
4143
4144 void
4145 PatchChangeDrag::setup_pointer_frame_offset ()
4146 {
4147         boost::shared_ptr<Region> region = _region_view->region ();
4148         _pointer_frame_offset = raw_grab_frame() - _region_view->beats_to_frames (_patch_change->patch()->time()) - region->position() + region->start();
4149 }
4150