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