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