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