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