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