enforce a minimum 5x5 pixel size for the rubber band rect
[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->canvas_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 () < rb->route()->order_key ();
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(_startdrag_video_offset+dt) - _startdrag_video_offset;
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                 /* This is asynchronous so it will be sent "now"
2381                  */
2382                 s->send_mmc_locate (f);
2383                 /* These are synchronous and will be sent during the next
2384                    process cycle
2385                 */
2386                 s->queue_full_time_code ();
2387                 s->queue_song_position_pointer ();
2388         }
2389
2390         show_verbose_cursor_time (t);
2391         _editor->UpdateAllTransportClocks (t);
2392 }
2393
2394 void
2395 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2396 {
2397         Drag::start_grab (event, c);
2398
2399         _grab_zoom = _editor->samples_per_pixel;
2400
2401         framepos_t where = _editor->canvas_event_frame (event);
2402
2403         _editor->snap_to_with_modifier (where, event);
2404
2405         _editor->_dragging_playhead = true;
2406
2407         Session* s = _editor->session ();
2408
2409         /* grab the track canvas item as well */
2410
2411         _cursor.track_canvas_item().grab();
2412
2413         if (s) {
2414                 if (_was_rolling && _stop) {
2415                         s->request_stop ();
2416                 }
2417
2418                 if (s->is_auditioning()) {
2419                         s->cancel_audition ();
2420                 }
2421
2422
2423                 if (AudioEngine::instance()->connected()) {
2424                         
2425                         /* do this only if we're the engine is connected
2426                          * because otherwise this request will never be
2427                          * serviced and we'll busy wait forever. likewise,
2428                          * notice if we are disconnected while waiting for the
2429                          * request to be serviced.
2430                          */
2431
2432                         s->request_suspend_timecode_transmission ();
2433                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2434                                 /* twiddle our thumbs */
2435                         }
2436                 }
2437         }
2438
2439         fake_locate (where);
2440 }
2441
2442 void
2443 CursorDrag::motion (GdkEvent* event, bool)
2444 {
2445         framepos_t const adjusted_frame = adjusted_current_frame (event);
2446         if (adjusted_frame != last_pointer_frame()) {
2447                 fake_locate (adjusted_frame);
2448         }
2449 }
2450
2451 void
2452 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2453 {
2454         _editor->_dragging_playhead = false;
2455
2456         _cursor.track_canvas_item().ungrab();
2457
2458         if (!movement_occurred && _stop) {
2459                 return;
2460         }
2461
2462         motion (event, false);
2463
2464         Session* s = _editor->session ();
2465         if (s) {
2466                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2467                 _editor->_pending_locate_request = true;
2468                 s->request_resume_timecode_transmission ();
2469         }
2470 }
2471
2472 void
2473 CursorDrag::aborted (bool)
2474 {
2475         _cursor.track_canvas_item().ungrab();
2476
2477         if (_editor->_dragging_playhead) {
2478                 _editor->session()->request_resume_timecode_transmission ();
2479                 _editor->_dragging_playhead = false;
2480         }
2481
2482         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2483 }
2484
2485 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2486         : RegionDrag (e, i, p, v)
2487 {
2488         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2489 }
2490
2491 void
2492 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2493 {
2494         Drag::start_grab (event, cursor);
2495
2496         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2497         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2498
2499         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2500 }
2501
2502 void
2503 FadeInDrag::setup_pointer_frame_offset ()
2504 {
2505         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2506         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2507         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2508 }
2509
2510 void
2511 FadeInDrag::motion (GdkEvent* event, bool)
2512 {
2513         framecnt_t fade_length;
2514
2515         framepos_t const pos = adjusted_current_frame (event);
2516
2517         boost::shared_ptr<Region> region = _primary->region ();
2518
2519         if (pos < (region->position() + 64)) {
2520                 fade_length = 64; // this should be a minimum defined somewhere
2521         } else if (pos > region->last_frame()) {
2522                 fade_length = region->length();
2523         } else {
2524                 fade_length = pos - region->position();
2525         }
2526
2527         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2528
2529                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2530
2531                 if (!tmp) {
2532                         continue;
2533                 }
2534
2535                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2536         }
2537
2538         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2539 }
2540
2541 void
2542 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2543 {
2544         if (!movement_occurred) {
2545                 return;
2546         }
2547
2548         framecnt_t fade_length;
2549
2550         framepos_t const pos = adjusted_current_frame (event);
2551
2552         boost::shared_ptr<Region> region = _primary->region ();
2553
2554         if (pos < (region->position() + 64)) {
2555                 fade_length = 64; // this should be a minimum defined somewhere
2556         } else if (pos > region->last_frame()) {
2557                 fade_length = region->length();
2558         } else {
2559                 fade_length = pos - region->position();
2560         }
2561
2562         _editor->begin_reversible_command (_("change fade in length"));
2563
2564         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2565
2566                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2567
2568                 if (!tmp) {
2569                         continue;
2570                 }
2571
2572                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
2573                 XMLNode &before = alist->get_state();
2574
2575                 tmp->audio_region()->set_fade_in_length (fade_length);
2576                 tmp->audio_region()->set_fade_in_active (true);
2577
2578                 XMLNode &after = alist->get_state();
2579                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2580         }
2581
2582         _editor->commit_reversible_command ();
2583 }
2584
2585 void
2586 FadeInDrag::aborted (bool)
2587 {
2588         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2589                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2590
2591                 if (!tmp) {
2592                         continue;
2593                 }
2594
2595                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
2596         }
2597 }
2598
2599 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2600         : RegionDrag (e, i, p, v)
2601 {
2602         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
2603 }
2604
2605 void
2606 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2607 {
2608         Drag::start_grab (event, cursor);
2609
2610         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2611         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2612
2613         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
2614 }
2615
2616 void
2617 FadeOutDrag::setup_pointer_frame_offset ()
2618 {
2619         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2620         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
2621         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
2622 }
2623
2624 void
2625 FadeOutDrag::motion (GdkEvent* event, bool)
2626 {
2627         framecnt_t fade_length;
2628
2629         framepos_t const pos = adjusted_current_frame (event);
2630
2631         boost::shared_ptr<Region> region = _primary->region ();
2632
2633         if (pos > (region->last_frame() - 64)) {
2634                 fade_length = 64; // this should really be a minimum fade defined somewhere
2635         }
2636         else if (pos < region->position()) {
2637                 fade_length = region->length();
2638         }
2639         else {
2640                 fade_length = region->last_frame() - pos;
2641         }
2642
2643         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2644
2645                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2646
2647                 if (!tmp) {
2648                         continue;
2649                 }
2650
2651                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
2652         }
2653
2654         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
2655 }
2656
2657 void
2658 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
2659 {
2660         if (!movement_occurred) {
2661                 return;
2662         }
2663
2664         framecnt_t fade_length;
2665
2666         framepos_t const pos = adjusted_current_frame (event);
2667
2668         boost::shared_ptr<Region> region = _primary->region ();
2669
2670         if (pos > (region->last_frame() - 64)) {
2671                 fade_length = 64; // this should really be a minimum fade defined somewhere
2672         }
2673         else if (pos < region->position()) {
2674                 fade_length = region->length();
2675         }
2676         else {
2677                 fade_length = region->last_frame() - pos;
2678         }
2679
2680         _editor->begin_reversible_command (_("change fade out length"));
2681
2682         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2683
2684                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2685
2686                 if (!tmp) {
2687                         continue;
2688                 }
2689
2690                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
2691                 XMLNode &before = alist->get_state();
2692
2693                 tmp->audio_region()->set_fade_out_length (fade_length);
2694                 tmp->audio_region()->set_fade_out_active (true);
2695
2696                 XMLNode &after = alist->get_state();
2697                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
2698         }
2699
2700         _editor->commit_reversible_command ();
2701 }
2702
2703 void
2704 FadeOutDrag::aborted (bool)
2705 {
2706         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2707                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2708
2709                 if (!tmp) {
2710                         continue;
2711                 }
2712
2713                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
2714         }
2715 }
2716
2717 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
2718         : Drag (e, i)
2719 {
2720         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
2721
2722         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
2723         assert (_marker);
2724
2725         _points.push_back (ArdourCanvas::Duple (0, 0));
2726         _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
2727 }
2728
2729 MarkerDrag::~MarkerDrag ()
2730 {
2731         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
2732                 delete i->location;
2733         }
2734 }
2735
2736 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
2737 {
2738         location = new Location (*l);
2739         markers.push_back (m);
2740         move_both = false;
2741 }
2742
2743 void
2744 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2745 {
2746         Drag::start_grab (event, cursor);
2747
2748         bool is_start;
2749
2750         Location *location = _editor->find_location_from_marker (_marker, is_start);
2751         _editor->_dragging_edit_point = true;
2752
2753         update_item (location);
2754
2755         // _drag_line->show();
2756         // _line->raise_to_top();
2757
2758         if (is_start) {
2759                 show_verbose_cursor_time (location->start());
2760         } else {
2761                 show_verbose_cursor_time (location->end());
2762         }
2763
2764         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
2765
2766         switch (op) {
2767         case Selection::Toggle:
2768                 /* we toggle on the button release */
2769                 break;
2770         case Selection::Set:
2771                 if (!_editor->selection->selected (_marker)) {
2772                         _editor->selection->set (_marker);
2773                 }
2774                 break;
2775         case Selection::Extend:
2776         {
2777                 Locations::LocationList ll;
2778                 list<Marker*> to_add;
2779                 framepos_t s, e;
2780                 _editor->selection->markers.range (s, e);
2781                 s = min (_marker->position(), s);
2782                 e = max (_marker->position(), e);
2783                 s = min (s, e);
2784                 e = max (s, e);
2785                 if (e < max_framepos) {
2786                         ++e;
2787                 }
2788                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
2789                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
2790                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
2791                         if (lm) {
2792                                 if (lm->start) {
2793                                         to_add.push_back (lm->start);
2794                                 }
2795                                 if (lm->end) {
2796                                         to_add.push_back (lm->end);
2797                                 }
2798                         }
2799                 }
2800                 if (!to_add.empty()) {
2801                         _editor->selection->add (to_add);
2802                 }
2803                 break;
2804         }
2805         case Selection::Add:
2806                 _editor->selection->add (_marker);
2807                 break;
2808         }
2809
2810         /* Set up copies for us to manipulate during the drag 
2811          */
2812
2813         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
2814
2815                 Location* l = _editor->find_location_from_marker (*i, is_start);
2816
2817                 if (!l) {
2818                         continue;
2819                 }
2820
2821                 if (l->is_mark()) {
2822                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2823                 } else {
2824                         /* range: check that the other end of the range isn't
2825                            already there.
2826                         */
2827                         CopiedLocationInfo::iterator x;
2828                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2829                                 if (*(*x).location == *l) {
2830                                         break;
2831                                 }
2832                         }
2833                         if (x == _copied_locations.end()) {
2834                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
2835                         } else {
2836                                 (*x).markers.push_back (*i);
2837                                 (*x).move_both = true;
2838                         }
2839                 }
2840                         
2841         }
2842 }
2843
2844 void
2845 MarkerDrag::setup_pointer_frame_offset ()
2846 {
2847         bool is_start;
2848         Location *location = _editor->find_location_from_marker (_marker, is_start);
2849         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
2850 }
2851
2852 void
2853 MarkerDrag::motion (GdkEvent* event, bool)
2854 {
2855         framecnt_t f_delta = 0;
2856         bool is_start;
2857         bool move_both = false;
2858         Location *real_location;
2859         Location *copy_location = 0;
2860
2861         framepos_t const newframe = adjusted_current_frame (event);
2862         framepos_t next = newframe;
2863
2864         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2865                 move_both = true;
2866         }
2867
2868         CopiedLocationInfo::iterator x;
2869
2870         /* find the marker we're dragging, and compute the delta */
2871
2872         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2873
2874                 copy_location = (*x).location;
2875
2876                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
2877
2878                         /* this marker is represented by this
2879                          * CopiedLocationMarkerInfo 
2880                          */
2881
2882                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
2883                                 /* que pasa ?? */
2884                                 return;
2885                         }
2886
2887                         if (real_location->is_mark()) {
2888                                 f_delta = newframe - copy_location->start();
2889                         } else {
2890
2891
2892                                 switch (_marker->type()) {
2893                                 case Marker::SessionStart:
2894                                 case Marker::RangeStart:
2895                                 case Marker::LoopStart:
2896                                 case Marker::PunchIn:
2897                                         f_delta = newframe - copy_location->start();
2898                                         break;
2899
2900                                 case Marker::SessionEnd:
2901                                 case Marker::RangeEnd:
2902                                 case Marker::LoopEnd:
2903                                 case Marker::PunchOut:
2904                                         f_delta = newframe - copy_location->end();
2905                                         break;
2906                                 default:
2907                                         /* what kind of marker is this ? */
2908                                         return;
2909                                 }
2910                         }
2911
2912                         break;
2913                 }
2914         }
2915
2916         if (x == _copied_locations.end()) {
2917                 /* hmm, impossible - we didn't find the dragged marker */
2918                 return;
2919         }
2920
2921         /* now move them all */
2922
2923         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
2924
2925                 copy_location = x->location;
2926
2927                 /* call this to find out if its the start or end */
2928
2929                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
2930                         continue;
2931                 }
2932
2933                 if (real_location->locked()) {
2934                         continue;
2935                 }
2936
2937                 if (copy_location->is_mark()) {
2938
2939                         /* now move it */
2940
2941                         copy_location->set_start (copy_location->start() + f_delta);
2942
2943                 } else {
2944                         
2945                         framepos_t new_start = copy_location->start() + f_delta;
2946                         framepos_t new_end = copy_location->end() + f_delta;
2947                         
2948                         if (is_start) { // start-of-range marker
2949                                 
2950                                 if (move_both || (*x).move_both) {
2951                                         copy_location->set_start (new_start);
2952                                         copy_location->set_end (new_end);
2953                                 } else  if (new_start < copy_location->end()) {
2954                                         copy_location->set_start (new_start);
2955                                 } else if (newframe > 0) {
2956                                         _editor->snap_to (next, 1, true);
2957                                         copy_location->set_end (next);
2958                                         copy_location->set_start (newframe);
2959                                 }
2960
2961                         } else { // end marker
2962
2963                                 if (move_both || (*x).move_both) {
2964                                         copy_location->set_end (new_end);
2965                                         copy_location->set_start (new_start);
2966                                 } else if (new_end > copy_location->start()) {
2967                                         copy_location->set_end (new_end);
2968                                 } else if (newframe > 0) {
2969                                         _editor->snap_to (next, -1, true);
2970                                         copy_location->set_start (next);
2971                                         copy_location->set_end (newframe);
2972                                 }
2973                         }
2974                 }
2975
2976                 update_item (copy_location);
2977                 
2978                 /* now lookup the actual GUI items used to display this
2979                  * location and move them to wherever the copy of the location
2980                  * is now. This means that the logic in ARDOUR::Location is
2981                  * still enforced, even though we are not (yet) modifying 
2982                  * the real Location itself.
2983                  */
2984                 
2985                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
2986
2987                 if (lm) {
2988                         lm->set_position (copy_location->start(), copy_location->end());
2989                 }
2990
2991         }
2992
2993         assert (!_copied_locations.empty());
2994
2995         show_verbose_cursor_time (newframe);
2996 }
2997
2998 void
2999 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3000 {
3001         if (!movement_occurred) {
3002                 
3003                 if (was_double_click()) {
3004                         cerr << "End of marker double click\n";
3005                 }
3006
3007                 /* just a click, do nothing but finish
3008                    off the selection process
3009                 */
3010
3011                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3012
3013                 switch (op) {
3014                 case Selection::Set:
3015                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3016                                 _editor->selection->set (_marker);
3017                         }
3018                         break;
3019
3020                 case Selection::Toggle:
3021                         /* we toggle on the button release, click only */
3022                         _editor->selection->toggle (_marker);
3023                         break;
3024
3025                 case Selection::Extend:
3026                 case Selection::Add:
3027                         break;
3028                 }
3029
3030                 return;
3031         }
3032
3033         _editor->_dragging_edit_point = false;
3034
3035         _editor->begin_reversible_command ( _("move marker") );
3036         XMLNode &before = _editor->session()->locations()->get_state();
3037
3038         MarkerSelection::iterator i;
3039         CopiedLocationInfo::iterator x;
3040         bool is_start;
3041
3042         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3043              x != _copied_locations.end() && i != _editor->selection->markers.end();
3044              ++i, ++x) {
3045
3046                 Location * location = _editor->find_location_from_marker (*i, is_start);
3047
3048                 if (location) {
3049
3050                         if (location->locked()) {
3051                                 return;
3052                         }
3053
3054                         if (location->is_mark()) {
3055                                 location->set_start (((*x).location)->start());
3056                         } else {
3057                                 location->set (((*x).location)->start(), ((*x).location)->end());
3058                         }
3059                 }
3060         }
3061
3062         XMLNode &after = _editor->session()->locations()->get_state();
3063         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3064         _editor->commit_reversible_command ();
3065 }
3066
3067 void
3068 MarkerDrag::aborted (bool)
3069 {
3070         /* XXX: TODO */
3071 }
3072
3073 void
3074 MarkerDrag::update_item (Location*)
3075 {
3076         /* noop */
3077 }
3078
3079 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3080         : Drag (e, i),
3081           _cumulative_x_drag (0),
3082           _cumulative_y_drag (0)
3083 {
3084         if (_zero_gain_fraction < 0.0) {
3085                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3086         }
3087
3088         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3089
3090         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3091         assert (_point);
3092 }
3093
3094
3095 void
3096 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3097 {
3098         Drag::start_grab (event, _editor->cursors()->fader);
3099
3100         // start the grab at the center of the control point so
3101         // the point doesn't 'jump' to the mouse after the first drag
3102         _fixed_grab_x = _point->get_x();
3103         _fixed_grab_y = _point->get_y();
3104
3105         float const fraction = 1 - (_point->get_y() / _point->line().height());
3106
3107         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3108
3109         _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
3110                                         event->button.x + 10, event->button.y + 10);
3111
3112         _editor->verbose_cursor()->show ();
3113
3114         _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3115
3116         if (!_point->can_slide ()) {
3117                 _x_constrained = true;
3118         }
3119 }
3120
3121 void
3122 ControlPointDrag::motion (GdkEvent* event, bool)
3123 {
3124         double dx = _drags->current_pointer_x() - last_pointer_x();
3125         double dy = _drags->current_pointer_y() - last_pointer_y();
3126
3127         if (event->button.state & Keyboard::SecondaryModifier) {
3128                 dx *= 0.1;
3129                 dy *= 0.1;
3130         }
3131
3132         /* coordinate in pixels relative to the start of the region (for region-based automation)
3133            or track (for track-based automation) */
3134         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3135         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3136
3137         // calculate zero crossing point. back off by .01 to stay on the
3138         // positive side of zero
3139         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3140
3141         // make sure we hit zero when passing through
3142         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3143                 cy = zero_gain_y;
3144         }
3145
3146         if (_x_constrained) {
3147                 cx = _fixed_grab_x;
3148         }
3149         if (_y_constrained) {
3150                 cy = _fixed_grab_y;
3151         }
3152
3153         _cumulative_x_drag = cx - _fixed_grab_x;
3154         _cumulative_y_drag = cy - _fixed_grab_y;
3155
3156         cx = max (0.0, cx);
3157         cy = max (0.0, cy);
3158         cy = min ((double) _point->line().height(), cy);
3159
3160         framepos_t cx_frames = _editor->pixel_to_sample (cx);
3161
3162         if (!_x_constrained) {
3163                 _editor->snap_to_with_modifier (cx_frames, event);
3164         }
3165
3166         cx_frames = min (cx_frames, _point->line().maximum_time());
3167
3168         float const fraction = 1.0 - (cy / _point->line().height());
3169
3170         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3171
3172         _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
3173 }
3174
3175 void
3176 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3177 {
3178         if (!movement_occurred) {
3179
3180                 /* just a click */
3181
3182                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3183                         _editor->reset_point_selection ();
3184                 }
3185
3186         } else {
3187                 motion (event, false);
3188         }
3189
3190         _point->line().end_drag (_pushing, _final_index);
3191         _editor->session()->commit_reversible_command ();
3192 }
3193
3194 void
3195 ControlPointDrag::aborted (bool)
3196 {
3197         _point->line().reset ();
3198 }
3199
3200 bool
3201 ControlPointDrag::active (Editing::MouseMode m)
3202 {
3203         if (m == Editing::MouseGain) {
3204                 /* always active in mouse gain */
3205                 return true;
3206         }
3207
3208         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3209         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3210 }
3211
3212 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3213         : Drag (e, i),
3214           _line (0),
3215           _cumulative_y_drag (0)
3216 {
3217         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3218 }
3219
3220 void
3221 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3222 {
3223         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3224         assert (_line);
3225
3226         _item = &_line->grab_item ();
3227
3228         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3229            origin, and ditto for y.
3230         */
3231
3232         double cx = event->button.x;
3233         double cy = event->button.y;
3234
3235         _line->parent_group().canvas_to_item (cx, cy);
3236
3237         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3238
3239         uint32_t before;
3240         uint32_t after;
3241
3242         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3243                 /* no adjacent points */
3244                 return;
3245         }
3246
3247         Drag::start_grab (event, _editor->cursors()->fader);
3248
3249         /* store grab start in parent frame */
3250
3251         _fixed_grab_x = cx;
3252         _fixed_grab_y = cy;
3253
3254         double fraction = 1.0 - (cy / _line->height());
3255
3256         _line->start_drag_line (before, after, fraction);
3257
3258         _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
3259                                         event->button.x + 10, event->button.y + 10);
3260
3261         _editor->verbose_cursor()->show ();
3262 }
3263
3264 void
3265 LineDrag::motion (GdkEvent* event, bool)
3266 {
3267         double dy = _drags->current_pointer_y() - last_pointer_y();
3268
3269         if (event->button.state & Keyboard::SecondaryModifier) {
3270                 dy *= 0.1;
3271         }
3272
3273         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3274
3275         _cumulative_y_drag = cy - _fixed_grab_y;
3276
3277         cy = max (0.0, cy);
3278         cy = min ((double) _line->height(), cy);
3279
3280         double const fraction = 1.0 - (cy / _line->height());
3281         uint32_t ignored;
3282
3283         /* we are ignoring x position for this drag, so we can just pass in anything */
3284         _line->drag_motion (0, fraction, true, false, ignored);
3285
3286         _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
3287 }
3288
3289 void
3290 LineDrag::finished (GdkEvent* event, bool)
3291 {
3292         motion (event, false);
3293         _line->end_drag (false, 0);
3294         _editor->session()->commit_reversible_command ();
3295 }
3296
3297 void
3298 LineDrag::aborted (bool)
3299 {
3300         _line->reset ();
3301 }
3302
3303 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3304         : Drag (e, i),
3305           _line (0),
3306           _cumulative_x_drag (0)
3307 {
3308         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3309 }
3310
3311 void
3312 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3313 {
3314         Drag::start_grab (event);
3315
3316         _line = reinterpret_cast<Line*> (_item);
3317         assert (_line);
3318
3319         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3320
3321         double cx = event->button.x;
3322         double cy = event->button.y;
3323
3324         _item->parent()->canvas_to_item (cx, cy);
3325
3326         /* store grab start in parent frame */
3327         _region_view_grab_x = cx;
3328
3329         _before = *(float*) _item->get_data ("position");
3330
3331         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3332
3333         _max_x = _editor->sample_to_pixel(_arv->get_duration());
3334 }
3335
3336 void
3337 FeatureLineDrag::motion (GdkEvent*, bool)
3338 {
3339         double dx = _drags->current_pointer_x() - last_pointer_x();
3340
3341         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3342
3343         _cumulative_x_drag += dx;
3344
3345         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3346
3347         if (cx > _max_x){
3348                 cx = _max_x;
3349         }
3350         else if(cx < 0){
3351                 cx = 0;
3352         }
3353
3354         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3355         assert (bbox);
3356         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3357
3358         float *pos = new float;
3359         *pos = cx;
3360
3361         _line->set_data ("position", pos);
3362
3363         _before = cx;
3364 }
3365
3366 void
3367 FeatureLineDrag::finished (GdkEvent*, bool)
3368 {
3369         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3370         _arv->update_transient(_before, _before);
3371 }
3372
3373 void
3374 FeatureLineDrag::aborted (bool)
3375 {
3376         //_line->reset ();
3377 }
3378
3379 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3380         : Drag (e, i)
3381         , _vertical_only (false)
3382 {
3383         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3384 }
3385
3386 void
3387 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3388 {
3389         Drag::start_grab (event);
3390         show_verbose_cursor_time (adjusted_current_frame (event));
3391 }
3392
3393 void
3394 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3395 {
3396         framepos_t start;
3397         framepos_t end;
3398         double y1;
3399         double y2;
3400
3401         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3402
3403         framepos_t grab = grab_frame ();
3404         if (Config->get_rubberbanding_snaps_to_grid ()) {
3405                 _editor->snap_to_with_modifier (grab, event);
3406         }
3407
3408         /* base start and end on initial click position */
3409
3410         if (pf < grab) {
3411                 start = pf;
3412                 end = grab;
3413         } else {
3414                 end = pf;
3415                 start = grab;
3416         }
3417
3418         if (_drags->current_pointer_y() < grab_y()) {
3419                 y1 = _drags->current_pointer_y();
3420                 y2 = grab_y();
3421         } else {
3422                 y2 = _drags->current_pointer_y();
3423                 y1 = grab_y();
3424         }
3425
3426
3427         if (start != end || y1 != y2) {
3428
3429                 double x1 = _editor->sample_to_pixel (start);
3430                 double x2 = _editor->sample_to_pixel (end);
3431
3432                 _editor->rubberband_rect->set_x0 (x1);
3433                 if (_vertical_only) {
3434                         /* fixed 10 pixel width */
3435                         _editor->rubberband_rect->set_x1 (x1 + 10);
3436                 } else {
3437                         if (x2 < x1) {
3438                                 x2 = min (x1 - 5, x2);
3439                         } else {
3440                                 x2 = max (x1 + 5, x2);
3441                         }
3442                         _editor->rubberband_rect->set_x1 (x2);
3443                 } 
3444
3445                 _editor->rubberband_rect->set_y0 (y1);
3446                 if (y2 < y1) {
3447                         y2 = min (y1 - 5, y2);
3448                 } else {
3449                         y2 = max (y1 + 5, y2);
3450                 }
3451
3452                 _editor->rubberband_rect->set_y1 (y2);
3453                 
3454                 _editor->rubberband_rect->show();
3455                 _editor->rubberband_rect->raise_to_top();
3456
3457                 show_verbose_cursor_time (pf);
3458
3459                 do_select_things (event, true);
3460         }
3461 }
3462
3463 void
3464 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3465 {
3466         framepos_t x1;
3467         framepos_t x2;
3468         
3469         if (grab_frame() < last_pointer_frame()) {
3470                 x1 = grab_frame ();
3471                 x2 = last_pointer_frame ();
3472         } else {
3473                 x2 = grab_frame ();
3474                 x1 = last_pointer_frame ();
3475         }
3476
3477         double y1;
3478         double y2;
3479         
3480         if (_drags->current_pointer_y() < grab_y()) {
3481                 y1 = _drags->current_pointer_y();
3482                 y2 = grab_y();
3483         } else {
3484                 y2 = _drags->current_pointer_y();
3485                 y1 = grab_y();
3486         }
3487
3488         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3489 }
3490
3491 void
3492 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3493 {
3494         if (movement_occurred) {
3495
3496                 motion (event, false);
3497                 do_select_things (event, false);
3498
3499         } else {
3500
3501                 /* just a click */
3502
3503                 bool do_deselect = true;
3504                 MidiTimeAxisView* mtv;
3505
3506                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3507                         /* MIDI track */
3508                         if (_editor->selection->empty()) {
3509                                 /* nothing selected */
3510                                 add_midi_region (mtv);
3511                                 do_deselect = false;
3512                         }
3513                 } 
3514
3515                 /* do not deselect if Primary or Tertiary (toggle-select or
3516                  * extend-select are pressed.
3517                  */
3518
3519                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) && 
3520                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) && 
3521                     do_deselect) {
3522                         deselect_things ();
3523                 }
3524
3525         }
3526
3527         _editor->rubberband_rect->hide();
3528 }
3529
3530 void
3531 RubberbandSelectDrag::aborted (bool)
3532 {
3533         _editor->rubberband_rect->hide ();
3534 }
3535
3536 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3537         : RegionDrag (e, i, p, v)
3538 {
3539         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3540 }
3541
3542 void
3543 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3544 {
3545         Drag::start_grab (event, cursor);
3546
3547         show_verbose_cursor_time (adjusted_current_frame (event));
3548 }
3549
3550 void
3551 TimeFXDrag::motion (GdkEvent* event, bool)
3552 {
3553         RegionView* rv = _primary;
3554         StreamView* cv = rv->get_time_axis_view().view ();
3555
3556         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3557         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3558         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3559
3560         framepos_t const pf = adjusted_current_frame (event);
3561
3562         if (pf > rv->region()->position()) {
3563                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3564         }
3565
3566         show_verbose_cursor_time (pf);
3567 }
3568
3569 void
3570 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3571 {
3572         _primary->get_time_axis_view().hide_timestretch ();
3573
3574         if (!movement_occurred) {
3575                 return;
3576         }
3577
3578         if (last_pointer_frame() < _primary->region()->position()) {
3579                 /* backwards drag of the left edge - not usable */
3580                 return;
3581         }
3582
3583         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3584
3585         float percentage = (double) newlen / (double) _primary->region()->length();
3586
3587 #ifndef USE_RUBBERBAND
3588         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3589         if (_primary->region()->data_type() == DataType::AUDIO) {
3590                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3591         }
3592 #endif
3593
3594         if (!_editor->get_selection().regions.empty()) {
3595                 /* primary will already be included in the selection, and edit
3596                    group shared editing will propagate selection across
3597                    equivalent regions, so just use the current region
3598                    selection.
3599                 */
3600
3601                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3602                         error << _("An error occurred while executing time stretch operation") << endmsg;
3603                 }
3604         }
3605 }
3606
3607 void
3608 TimeFXDrag::aborted (bool)
3609 {
3610         _primary->get_time_axis_view().hide_timestretch ();
3611 }
3612
3613 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3614         : Drag (e, i)
3615 {
3616         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3617 }
3618
3619 void
3620 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3621 {
3622         Drag::start_grab (event);
3623 }
3624
3625 void
3626 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3627 {
3628         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3629 }
3630
3631 void
3632 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3633 {
3634         if (movement_occurred && _editor->session()) {
3635                 /* make sure we stop */
3636                 _editor->session()->request_transport_speed (0.0);
3637         }
3638 }
3639
3640 void
3641 ScrubDrag::aborted (bool)
3642 {
3643         /* XXX: TODO */
3644 }
3645
3646 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3647         : Drag (e, i)
3648         , _operation (o)
3649         , _add (false)
3650         , _extend (false)
3651         , _original_pointer_time_axis (-1)
3652         , _last_pointer_time_axis (-1)
3653         , _time_selection_at_start (!_editor->get_selection().time.empty())
3654 {
3655         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3656         
3657         if (_time_selection_at_start) {
3658                 start_at_start = _editor->get_selection().time.start();
3659                 end_at_start = _editor->get_selection().time.end_frame();
3660         }
3661 }
3662
3663 void
3664 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3665 {
3666         if (_editor->session() == 0) {
3667                 return;
3668         }
3669
3670         Gdk::Cursor* cursor = 0;
3671
3672         switch (_operation) {
3673         case CreateSelection:
3674                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3675                         _add = true;
3676                 } else {
3677                         _add = false;
3678                 }
3679                 cursor = _editor->cursors()->selector;
3680                 Drag::start_grab (event, cursor);
3681                 break;
3682
3683         case SelectionStartTrim:
3684                 if (_editor->clicked_axisview) {
3685                         _editor->clicked_axisview->order_selection_trims (_item, true);
3686                 }
3687                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3688                 break;
3689
3690         case SelectionEndTrim:
3691                 if (_editor->clicked_axisview) {
3692                         _editor->clicked_axisview->order_selection_trims (_item, false);
3693                 }
3694                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3695                 break;
3696
3697         case SelectionMove:
3698                 Drag::start_grab (event, cursor);
3699                 break;
3700
3701         case SelectionExtend:
3702                 Drag::start_grab (event, cursor);
3703                 break;
3704         }
3705
3706         if (_operation == SelectionMove) {
3707                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3708         } else {
3709                 show_verbose_cursor_time (adjusted_current_frame (event));
3710         }
3711
3712         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3713 }
3714
3715 void
3716 SelectionDrag::setup_pointer_frame_offset ()
3717 {
3718         switch (_operation) {
3719         case CreateSelection:
3720                 _pointer_frame_offset = 0;
3721                 break;
3722
3723         case SelectionStartTrim:
3724         case SelectionMove:
3725                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3726                 break;
3727
3728         case SelectionEndTrim:
3729                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3730                 break;
3731
3732         case SelectionExtend:
3733                 break;
3734         }
3735 }
3736
3737 void
3738 SelectionDrag::motion (GdkEvent* event, bool first_move)
3739 {
3740         framepos_t start = 0;
3741         framepos_t end = 0;
3742         framecnt_t length = 0;
3743         framecnt_t distance = 0;
3744
3745         pair<TimeAxisView*, int> const pending_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ());
3746         if (pending_time_axis.first == 0) {
3747                 return;
3748         }
3749
3750         framepos_t const pending_position = adjusted_current_frame (event);
3751
3752         /* only alter selection if things have changed */
3753
3754         if (pending_time_axis.first->order() == _last_pointer_time_axis && pending_position == last_pointer_frame()) {
3755                 return;
3756         }
3757
3758         switch (_operation) {
3759         case CreateSelection:
3760         {
3761                 framepos_t grab = grab_frame ();
3762
3763                 if (first_move) {
3764                         grab = adjusted_current_frame (event, false);
3765                         if (grab < pending_position) {
3766                                 _editor->snap_to (grab, -1);
3767                         }  else {
3768                                 _editor->snap_to (grab, 1);
3769                         }
3770                 }
3771
3772                 if (pending_position < grab) {
3773                         start = pending_position;
3774                         end = grab;
3775                 } else {
3776                         end = pending_position;
3777                         start = grab;
3778                 }
3779
3780                 /* first drag: Either add to the selection
3781                    or create a new selection
3782                 */
3783
3784                 if (first_move) {
3785
3786                         if (_add) {
3787                                 /* adding to the selection */
3788                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3789                                 //_editor->selection->add (_editor->clicked_axisview);
3790                                 _editor->clicked_selection = _editor->selection->add (start, end);
3791                                 _add = false;
3792                         } else {
3793                                 /* new selection */
3794
3795                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3796                                         //_editor->selection->set (_editor->clicked_axisview);
3797                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3798                                 }
3799
3800                                 _editor->clicked_selection = _editor->selection->set (start, end);
3801                         }
3802                 }
3803
3804                 /* select the track that we're in */
3805                 if (find (_added_time_axes.begin(), _added_time_axes.end(), pending_time_axis.first) == _added_time_axes.end()) {
3806                         // _editor->set_selected_track_as_side_effect (Selection::Add);
3807                         _editor->selection->add (pending_time_axis.first);
3808                         _added_time_axes.push_back (pending_time_axis.first);
3809                 }
3810
3811                 /* deselect any tracks that this drag no longer includes, being careful to only deselect
3812                    tracks that we selected in the first place.
3813                 */
3814
3815                 int min_order = min (_original_pointer_time_axis, pending_time_axis.first->order());
3816                 int max_order = max (_original_pointer_time_axis, pending_time_axis.first->order());
3817
3818                 list<TimeAxisView*>::iterator i = _added_time_axes.begin();
3819                 while (i != _added_time_axes.end()) {
3820
3821                         list<TimeAxisView*>::iterator tmp = i;
3822                         ++tmp;
3823
3824                         if ((*i)->order() < min_order || (*i)->order() > max_order) {
3825                                 _editor->selection->remove (*i);
3826                                 _added_time_axes.remove (*i);
3827                         }
3828
3829                         i = tmp;
3830                 }
3831
3832         }
3833         break;
3834
3835         case SelectionStartTrim:
3836
3837                 start = _editor->selection->time[_editor->clicked_selection].start;
3838                 end = _editor->selection->time[_editor->clicked_selection].end;
3839
3840                 if (pending_position > end) {
3841                         start = end;
3842                 } else {
3843                         start = pending_position;
3844                 }
3845                 break;
3846
3847         case SelectionEndTrim:
3848
3849                 start = _editor->selection->time[_editor->clicked_selection].start;
3850                 end = _editor->selection->time[_editor->clicked_selection].end;
3851
3852                 if (pending_position < start) {
3853                         end = start;
3854                 } else {
3855                         end = pending_position;
3856                 }
3857
3858                 break;
3859                 
3860         case SelectionMove:
3861
3862                 start = _editor->selection->time[_editor->clicked_selection].start;
3863                 end = _editor->selection->time[_editor->clicked_selection].end;
3864
3865                 length = end - start;
3866                 distance = pending_position - start;
3867                 start = pending_position;
3868                 _editor->snap_to (start);
3869
3870                 end = start + length;
3871
3872                 break;
3873
3874         case SelectionExtend:
3875                 break;
3876         }
3877
3878         if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3879                 _editor->start_canvas_autoscroll (1, 0);
3880         }
3881
3882         if (start != end) {
3883                 switch (_operation) {
3884                 case SelectionMove:     
3885                         if (_time_selection_at_start) {
3886                                 _editor->selection->move_time (distance);
3887                         }
3888                         break;
3889                 default:
3890                         _editor->selection->replace (_editor->clicked_selection, start, end);
3891                 }
3892         }
3893
3894         if (_operation == SelectionMove) {
3895                 show_verbose_cursor_time(start);
3896         } else {
3897                 show_verbose_cursor_time(pending_position);
3898         }
3899 }
3900
3901 void
3902 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3903 {
3904         Session* s = _editor->session();
3905
3906         if (movement_occurred) {
3907                 motion (event, false);
3908                 /* XXX this is not object-oriented programming at all. ick */
3909                 if (_editor->selection->time.consolidate()) {
3910                         _editor->selection->TimeChanged ();
3911                 }
3912
3913                 /* XXX what if its a music time selection? */
3914                 if (s) {
3915                         if ( s->get_play_range() && s->transport_rolling() ) {
3916                                 s->request_play_range (&_editor->selection->time, true);
3917                         } else {
3918                                 if (Config->get_always_play_range() && !s->transport_rolling()) {
3919                                         s->request_locate (_editor->get_selection().time.start());
3920                                 }
3921                         }
3922                 }
3923
3924         } else {
3925                 /* just a click, no pointer movement.
3926                  */
3927
3928                 if (_operation == SelectionExtend) {
3929                         if (_time_selection_at_start) {
3930                                 framepos_t pos = adjusted_current_frame (event, false);
3931                                 framepos_t start = min (pos, start_at_start);
3932                                 framepos_t end = max (pos, end_at_start);
3933                                 _editor->selection->set (start, end);
3934                         }
3935                 } else {
3936                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3937                                 if (_editor->clicked_selection) {
3938                                         _editor->selection->remove (_editor->clicked_selection);
3939                                 }
3940                         } else {
3941                                 if (!_editor->clicked_selection) {
3942                                         _editor->selection->clear_time();
3943                                 }
3944                         }
3945                 }
3946
3947                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3948                         _editor->selection->set (_editor->clicked_axisview);
3949                 }
3950                         
3951                 if (s && s->get_play_range () && s->transport_rolling()) {
3952                         s->request_stop (false, false);
3953                 }
3954
3955         }
3956
3957         _editor->stop_canvas_autoscroll ();
3958         _editor->clicked_selection = 0;
3959 }
3960
3961 void
3962 SelectionDrag::aborted (bool)
3963 {
3964         /* XXX: TODO */
3965 }
3966
3967 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3968         : Drag (e, i),
3969           _operation (o),
3970           _copy (false)
3971 {
3972         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
3973
3974         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group, 
3975                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
3976                                                                       physical_screen_height (_editor->get_window())));
3977         _drag_rect->hide ();
3978
3979         _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3980         _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
3981 }
3982
3983 void
3984 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3985 {
3986         if (_editor->session() == 0) {
3987                 return;
3988         }
3989
3990         Gdk::Cursor* cursor = 0;
3991
3992         if (!_editor->temp_location) {
3993                 _editor->temp_location = new Location (*_editor->session());
3994         }
3995
3996         switch (_operation) {
3997         case CreateRangeMarker:
3998         case CreateTransportMarker:
3999         case CreateCDMarker:
4000
4001                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4002                         _copy = true;
4003                 } else {
4004                         _copy = false;
4005                 }
4006                 cursor = _editor->cursors()->selector;
4007                 break;
4008         }
4009
4010         Drag::start_grab (event, cursor);
4011
4012         show_verbose_cursor_time (adjusted_current_frame (event));
4013 }
4014
4015 void
4016 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4017 {
4018         framepos_t start = 0;
4019         framepos_t end = 0;
4020         ArdourCanvas::Rectangle *crect;
4021
4022         switch (_operation) {
4023         case CreateRangeMarker:
4024                 crect = _editor->range_bar_drag_rect;
4025                 break;
4026         case CreateTransportMarker:
4027                 crect = _editor->transport_bar_drag_rect;
4028                 break;
4029         case CreateCDMarker:
4030                 crect = _editor->cd_marker_bar_drag_rect;
4031                 break;
4032         default:
4033                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4034                 return;
4035                 break;
4036         }
4037
4038         framepos_t const pf = adjusted_current_frame (event);
4039
4040         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4041                 framepos_t grab = grab_frame ();
4042                 _editor->snap_to (grab);
4043
4044                 if (pf < grab_frame()) {
4045                         start = pf;
4046                         end = grab;
4047                 } else {
4048                         end = pf;
4049                         start = grab;
4050                 }
4051
4052                 /* first drag: Either add to the selection
4053                    or create a new selection.
4054                 */
4055
4056                 if (first_move) {
4057
4058                         _editor->temp_location->set (start, end);
4059
4060                         crect->show ();
4061
4062                         update_item (_editor->temp_location);
4063                         _drag_rect->show();
4064                         //_drag_rect->raise_to_top();
4065
4066                 }
4067         }
4068
4069         if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4070                 _editor->start_canvas_autoscroll (1, 0);
4071         }
4072
4073         if (start != end) {
4074                 _editor->temp_location->set (start, end);
4075
4076                 double x1 = _editor->sample_to_pixel (start);
4077                 double x2 = _editor->sample_to_pixel (end);
4078                 crect->set_x0 (x1);
4079                 crect->set_x1 (x2);
4080
4081                 update_item (_editor->temp_location);
4082         }
4083
4084         show_verbose_cursor_time (pf);
4085
4086 }
4087
4088 void
4089 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4090 {
4091         Location * newloc = 0;
4092         string rangename;
4093         int flags;
4094
4095         if (movement_occurred) {
4096                 motion (event, false);
4097                 _drag_rect->hide();
4098
4099                 switch (_operation) {
4100                 case CreateRangeMarker:
4101                 case CreateCDMarker:
4102                     {
4103                         _editor->begin_reversible_command (_("new range marker"));
4104                         XMLNode &before = _editor->session()->locations()->get_state();
4105                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
4106                         if (_operation == CreateCDMarker) {
4107                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
4108                                 _editor->cd_marker_bar_drag_rect->hide();
4109                         }
4110                         else {
4111                                 flags = Location::IsRangeMarker;
4112                                 _editor->range_bar_drag_rect->hide();
4113                         }
4114                         newloc = new Location (
4115                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4116                                 );
4117
4118                         _editor->session()->locations()->add (newloc, true);
4119                         XMLNode &after = _editor->session()->locations()->get_state();
4120                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4121                         _editor->commit_reversible_command ();
4122                         break;
4123                     }
4124
4125                 case CreateTransportMarker:
4126                         // popup menu to pick loop or punch
4127                         _editor->new_transport_marker_context_menu (&event->button, _item);
4128                         break;
4129                 }
4130         } else {
4131                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4132
4133                 if (Keyboard::no_modifier_keys_pressed (&event->button) && _operation != CreateCDMarker) {
4134
4135                         framepos_t start;
4136                         framepos_t end;
4137
4138                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4139
4140                         if (end == max_framepos) {
4141                                 end = _editor->session()->current_end_frame ();
4142                         }
4143
4144                         if (start == max_framepos) {
4145                                 start = _editor->session()->current_start_frame ();
4146                         }
4147
4148                         switch (_editor->mouse_mode) {
4149                         case MouseObject:
4150                                 /* find the two markers on either side and then make the selection from it */
4151                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4152                                 break;
4153
4154                         case MouseRange:
4155                                 /* find the two markers on either side of the click and make the range out of it */
4156                                 _editor->selection->set (start, end);
4157                                 break;
4158
4159                         default:
4160                                 break;
4161                         }
4162                 }
4163         }
4164
4165         _editor->stop_canvas_autoscroll ();
4166 }
4167
4168 void
4169 RangeMarkerBarDrag::aborted (bool)
4170 {
4171         /* XXX: TODO */
4172 }
4173
4174 void
4175 RangeMarkerBarDrag::update_item (Location* location)
4176 {
4177         double const x1 = _editor->sample_to_pixel (location->start());
4178         double const x2 = _editor->sample_to_pixel (location->end());
4179
4180         _drag_rect->set_x0 (x1);
4181         _drag_rect->set_x1 (x2);
4182 }
4183
4184 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4185         : Drag (e, i)
4186         , _zoom_out (false)
4187 {
4188         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4189 }
4190
4191 void
4192 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4193 {
4194         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4195                 Drag::start_grab (event, _editor->cursors()->zoom_out);
4196                 _zoom_out = true;
4197         } else {
4198                 Drag::start_grab (event, _editor->cursors()->zoom_in);
4199                 _zoom_out = false;
4200         }
4201
4202         show_verbose_cursor_time (adjusted_current_frame (event));
4203 }
4204
4205 void
4206 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4207 {
4208         framepos_t start;
4209         framepos_t end;
4210
4211         framepos_t const pf = adjusted_current_frame (event);
4212
4213         framepos_t grab = grab_frame ();
4214         _editor->snap_to_with_modifier (grab, event);
4215
4216         /* base start and end on initial click position */
4217         if (pf < grab) {
4218                 start = pf;
4219                 end = grab;
4220         } else {
4221                 end = pf;
4222                 start = grab;
4223         }
4224
4225         if (start != end) {
4226
4227                 if (first_move) {
4228                         _editor->zoom_rect->show();
4229                         _editor->zoom_rect->raise_to_top();
4230                 }
4231
4232                 _editor->reposition_zoom_rect(start, end);
4233
4234                 show_verbose_cursor_time (pf);
4235         }
4236 }
4237
4238 void
4239 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4240 {
4241         if (movement_occurred) {
4242                 motion (event, false);
4243
4244                 if (grab_frame() < last_pointer_frame()) {
4245                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4246                 } else {
4247                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4248                 }
4249         } else {
4250                 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4251                         _editor->tav_zoom_step (_zoom_out);
4252                 } else {
4253                         _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4254                 }
4255         }
4256
4257         _editor->zoom_rect->hide();
4258 }
4259
4260 void
4261 MouseZoomDrag::aborted (bool)
4262 {
4263         _editor->zoom_rect->hide ();
4264 }
4265
4266 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4267         : Drag (e, i)
4268         , _cumulative_dx (0)
4269         , _cumulative_dy (0)
4270 {
4271         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4272
4273         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4274         assert (_primary);
4275         _region = &_primary->region_view ();
4276         _note_height = _region->midi_stream_view()->note_height ();
4277 }
4278
4279 void
4280 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4281 {
4282         Drag::start_grab (event);
4283
4284         if (!(_was_selected = _primary->selected())) {
4285
4286                 /* tertiary-click means extend selection - we'll do that on button release,
4287                    so don't add it here, because otherwise we make it hard to figure
4288                    out the "extend-to" range.
4289                 */
4290
4291                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4292
4293                 if (!extend) {
4294                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4295
4296                         if (add) {
4297                                 _region->note_selected (_primary, true);
4298                         } else {
4299                                 _region->unique_select (_primary);
4300                         }
4301                 }
4302         }
4303 }
4304
4305 /** @return Current total drag x change in frames */
4306 frameoffset_t
4307 NoteDrag::total_dx () const
4308 {
4309         /* dx in frames */
4310         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4311
4312         /* primary note time */
4313         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4314
4315         /* new time of the primary note in session frames */
4316         frameoffset_t st = n + dx;
4317
4318         framepos_t const rp = _region->region()->position ();
4319
4320         /* prevent the note being dragged earlier than the region's position */
4321         st = max (st, rp);
4322
4323         /* snap and return corresponding delta */
4324         return _region->snap_frame_to_frame (st - rp) + rp - n;
4325 }
4326
4327 /** @return Current total drag y change in note number */
4328 int8_t
4329 NoteDrag::total_dy () const
4330 {
4331         MidiStreamView* msv = _region->midi_stream_view ();
4332         double const y = _region->midi_view()->y_position ();
4333         /* new current note */
4334         uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4335         /* clamp */
4336         n = max (msv->lowest_note(), n);
4337         n = min (msv->highest_note(), n);
4338         /* and work out delta */
4339         return n - msv->y_to_note (grab_y() - y);
4340 }
4341
4342 void
4343 NoteDrag::motion (GdkEvent *, bool)
4344 {
4345         /* Total change in x and y since the start of the drag */
4346         frameoffset_t const dx = total_dx ();
4347         int8_t const dy = total_dy ();
4348
4349         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4350         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4351         double const tdy = -dy * _note_height - _cumulative_dy;
4352
4353         if (tdx || tdy) {
4354                 _cumulative_dx += tdx;
4355                 _cumulative_dy += tdy;
4356
4357                 int8_t note_delta = total_dy();
4358
4359                 _region->move_selection (tdx, tdy, note_delta);
4360
4361                 /* the new note value may be the same as the old one, but we
4362                  * don't know what that means because the selection may have
4363                  * involved more than one note and we might be doing something
4364                  * odd with them. so show the note value anyway, always.
4365                  */
4366
4367                 char buf[12];
4368                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4369                 
4370                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4371                           (int) floor (new_note));
4372
4373                 show_verbose_cursor_text (buf);
4374         }
4375 }
4376
4377 void
4378 NoteDrag::finished (GdkEvent* ev, bool moved)
4379 {
4380         if (!moved) {
4381                 /* no motion - select note */
4382                 
4383                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4384                     _editor->current_mouse_mode() == Editing::MouseDraw) {
4385                         
4386                         if (_was_selected) {
4387                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4388                                 if (add) {
4389                                         _region->note_deselected (_primary);
4390                                 }
4391                         } else {
4392                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4393                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4394
4395                                 if (!extend && !add && _region->selection_size() > 1) {
4396                                         _region->unique_select (_primary);
4397                                 } else if (extend) {
4398                                         _region->note_selected (_primary, true, true);
4399                                 } else {
4400                                         /* it was added during button press */
4401                                 }
4402                         }
4403                 }
4404         } else {
4405                 _region->note_dropped (_primary, total_dx(), total_dy());
4406         }
4407 }
4408
4409 void
4410 NoteDrag::aborted (bool)
4411 {
4412         /* XXX: TODO */
4413 }
4414
4415 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4416 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4417         : Drag (editor, atv->base_item ())
4418         , _ranges (r)
4419         , _nothing_to_drag (false)
4420 {
4421         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4422         y_origin = atv->y_position();
4423         setup (atv->lines ());
4424 }
4425
4426 /** Make an AutomationRangeDrag for region gain lines */
4427 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4428         : Drag (editor, rv->get_canvas_group ())
4429         , _ranges (r)
4430         , _nothing_to_drag (false)
4431 {
4432         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4433
4434         list<boost::shared_ptr<AutomationLine> > lines;
4435         lines.push_back (rv->get_gain_line ());
4436         y_origin = rv->get_time_axis_view().y_position();
4437         setup (lines);
4438 }
4439
4440 /** @param lines AutomationLines to drag.
4441  *  @param offset Offset from the session start to the points in the AutomationLines.
4442  */
4443 void
4444 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4445 {
4446         /* find the lines that overlap the ranges being dragged */
4447         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4448         while (i != lines.end ()) {
4449                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4450                 ++j;
4451
4452                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4453
4454                 /* check this range against all the AudioRanges that we are using */
4455                 list<AudioRange>::const_iterator k = _ranges.begin ();
4456                 while (k != _ranges.end()) {
4457                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4458                                 break;
4459                         }
4460                         ++k;
4461                 }
4462
4463                 /* add it to our list if it overlaps at all */
4464                 if (k != _ranges.end()) {
4465                         Line n;
4466                         n.line = *i;
4467                         n.state = 0;
4468                         n.range = r;
4469                         _lines.push_back (n);
4470                 }
4471
4472                 i = j;
4473         }
4474
4475         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4476 }
4477
4478 double
4479 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4480 {
4481         return 1.0 - ((global_y - y_origin) / line->height());
4482 }
4483
4484 void
4485 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4486 {
4487         Drag::start_grab (event, cursor);
4488
4489         /* Get line states before we start changing things */
4490         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4491                 i->state = &i->line->get_state ();
4492                 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4493         }
4494
4495         if (_ranges.empty()) {
4496
4497                 /* No selected time ranges: drag all points */
4498                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4499                         uint32_t const N = i->line->npoints ();
4500                         for (uint32_t j = 0; j < N; ++j) {
4501                                 i->points.push_back (i->line->nth (j));
4502                         }
4503                 }
4504
4505         } else {
4506
4507                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4508
4509                         framecnt_t const half = (i->start + i->end) / 2;
4510
4511                         /* find the line that this audio range starts in */
4512                         list<Line>::iterator j = _lines.begin();
4513                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4514                                 ++j;
4515                         }
4516
4517                         if (j != _lines.end()) {
4518                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4519
4520                                 /* j is the line that this audio range starts in; fade into it;
4521                                    64 samples length plucked out of thin air.
4522                                 */
4523
4524                                 framepos_t a = i->start + 64;
4525                                 if (a > half) {
4526                                         a = half;
4527                                 }
4528
4529                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4530                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4531
4532                                 the_list->add (p, the_list->eval (p));
4533                                 the_list->add (q, the_list->eval (q));
4534                         }
4535
4536                         /* same thing for the end */
4537
4538                         j = _lines.begin();
4539                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4540                                 ++j;
4541                         }
4542
4543                         if (j != _lines.end()) {
4544                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4545
4546                                 /* j is the line that this audio range starts in; fade out of it;
4547                                    64 samples length plucked out of thin air.
4548                                 */
4549
4550                                 framepos_t b = i->end - 64;
4551                                 if (b < half) {
4552                                         b = half;
4553                                 }
4554
4555                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4556                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4557
4558                                 the_list->add (p, the_list->eval (p));
4559                                 the_list->add (q, the_list->eval (q));
4560                         }
4561                 }
4562
4563                 _nothing_to_drag = true;
4564
4565                 /* Find all the points that should be dragged and put them in the relevant
4566                    points lists in the Line structs.
4567                 */
4568
4569                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4570
4571                         uint32_t const N = i->line->npoints ();
4572                         for (uint32_t j = 0; j < N; ++j) {
4573
4574                                 /* here's a control point on this line */
4575                                 ControlPoint* p = i->line->nth (j);
4576                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4577
4578                                 /* see if it's inside a range */
4579                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4580                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4581                                         ++k;
4582                                 }
4583
4584                                 if (k != _ranges.end()) {
4585                                         /* dragging this point */
4586                                         _nothing_to_drag = false;
4587                                         i->points.push_back (p);
4588                                 }
4589                         }
4590                 }
4591         }
4592
4593         if (_nothing_to_drag) {
4594                 return;
4595         }
4596
4597         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4598                 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4599         }
4600 }
4601
4602 void
4603 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4604 {
4605         if (_nothing_to_drag) {
4606                 return;
4607         }
4608
4609         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4610                 float const f = y_fraction (l->line, _drags->current_pointer_y());
4611                 /* we are ignoring x position for this drag, so we can just pass in anything */
4612                 uint32_t ignored;
4613                 l->line->drag_motion (0, f, true, false, ignored);
4614                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4615         }
4616 }
4617
4618 void
4619 AutomationRangeDrag::finished (GdkEvent* event, bool)
4620 {
4621         if (_nothing_to_drag) {
4622                 return;
4623         }
4624
4625         motion (event, false);
4626         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4627                 i->line->end_drag (false, 0);
4628         }
4629
4630         _editor->session()->commit_reversible_command ();
4631 }
4632
4633 void
4634 AutomationRangeDrag::aborted (bool)
4635 {
4636         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4637                 i->line->reset ();
4638         }
4639 }
4640
4641 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4642         : view (v)
4643 {
4644         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4645         layer = v->region()->layer ();
4646         initial_y = v->get_canvas_group()->position().y;
4647         initial_playlist = v->region()->playlist ();
4648         initial_position = v->region()->position ();
4649         initial_end = v->region()->position () + v->region()->length ();
4650 }
4651
4652 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4653         : Drag (e, i->canvas_item ())
4654         , _region_view (r)
4655         , _patch_change (i)
4656         , _cumulative_dx (0)
4657 {
4658         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4659                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4660                                                    grab_frame()));
4661 }
4662
4663 void
4664 PatchChangeDrag::motion (GdkEvent* ev, bool)
4665 {
4666         framepos_t f = adjusted_current_frame (ev);
4667         boost::shared_ptr<Region> r = _region_view->region ();
4668         f = max (f, r->position ());
4669         f = min (f, r->last_frame ());
4670
4671         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4672         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4673         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4674         _cumulative_dx = dxu;
4675 }
4676
4677 void
4678 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4679 {
4680         if (!movement_occurred) {
4681                 return;
4682         }
4683
4684         boost::shared_ptr<Region> r (_region_view->region ());
4685         framepos_t f = adjusted_current_frame (ev);
4686         f = max (f, r->position ());
4687         f = min (f, r->last_frame ());
4688
4689         _region_view->move_patch_change (
4690                 *_patch_change,
4691                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4692                 );
4693 }
4694
4695 void
4696 PatchChangeDrag::aborted (bool)
4697 {
4698         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4699 }
4700
4701 void
4702 PatchChangeDrag::setup_pointer_frame_offset ()
4703 {
4704         boost::shared_ptr<Region> region = _region_view->region ();
4705         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4706 }
4707
4708 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4709         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4710         , _region_view (rv)
4711 {
4712
4713 }
4714
4715 void
4716 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4717 {
4718         framepos_t const p = _region_view->region()->position ();
4719         double const y = _region_view->midi_view()->y_position ();
4720
4721         x1 = max ((framepos_t) 0, x1 - p);
4722         x2 = max ((framepos_t) 0, x2 - p);
4723         y1 = max (0.0, y1 - y);
4724         y2 = max (0.0, y2 - y);
4725         
4726         _region_view->update_drag_selection (
4727                 _editor->sample_to_pixel (x1),
4728                 _editor->sample_to_pixel (x2),
4729                 y1,
4730                 y2,
4731                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4732                 );
4733 }
4734
4735 void
4736 MidiRubberbandSelectDrag::deselect_things ()
4737 {
4738         /* XXX */
4739 }
4740
4741 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4742         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4743         , _region_view (rv)
4744 {
4745         _vertical_only = true;
4746 }
4747
4748 void
4749 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4750 {
4751         double const y = _region_view->midi_view()->y_position ();
4752
4753         y1 = max (0.0, y1 - y);
4754         y2 = max (0.0, y2 - y);
4755         
4756         _region_view->update_vertical_drag_selection (
4757                 y1,
4758                 y2,
4759                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4760                 );
4761 }
4762
4763 void
4764 MidiVerticalSelectDrag::deselect_things ()
4765 {
4766         /* XXX */
4767 }
4768
4769 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4770         : RubberbandSelectDrag (e, i)
4771 {
4772
4773 }
4774
4775 void
4776 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4777 {
4778         if (drag_in_progress) {
4779                 /* We just want to select things at the end of the drag, not during it */
4780                 return;
4781         }
4782         
4783         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4784         
4785         _editor->begin_reversible_command (_("rubberband selection"));
4786         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4787         _editor->commit_reversible_command ();
4788 }
4789
4790 void
4791 EditorRubberbandSelectDrag::deselect_things ()
4792 {
4793         if (!getenv("ARDOUR_SAE")) {
4794                 _editor->selection->clear_tracks();
4795         }
4796         _editor->selection->clear_regions();
4797         _editor->selection->clear_points ();
4798         _editor->selection->clear_lines ();
4799 }
4800
4801 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4802         : Drag (e, i)
4803         , _region_view (rv)
4804         , _drag_rect (0)
4805 {
4806         
4807 }
4808
4809 NoteCreateDrag::~NoteCreateDrag ()
4810 {
4811         delete _drag_rect;
4812 }
4813
4814 framecnt_t
4815 NoteCreateDrag::grid_frames (framepos_t t) const
4816 {
4817         bool success;
4818         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4819         if (!success) {
4820                 grid_beats = 1;
4821         }
4822
4823         return _region_view->region_beats_to_region_frames (grid_beats);
4824 }
4825
4826 void
4827 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4828 {
4829         Drag::start_grab (event, cursor);
4830                                                  
4831         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4832
4833         framepos_t pf = _drags->current_pointer_frame ();
4834         framecnt_t const g = grid_frames (pf);
4835
4836         /* Hack so that we always snap to the note that we are over, instead of snapping
4837            to the next one if we're more than halfway through the one we're over.
4838         */
4839         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4840                 pf -= g / 2;
4841         }
4842
4843         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4844
4845         MidiStreamView* sv = _region_view->midi_stream_view ();
4846         double const x = _editor->sample_to_pixel (_note[0]);
4847         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4848
4849         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4850         _drag_rect->set_outline_what (0xff);
4851         _drag_rect->set_outline_color (0xffffff99);
4852         _drag_rect->set_fill_color (0xffffff66);
4853 }
4854
4855 void
4856 NoteCreateDrag::motion (GdkEvent* event, bool)
4857 {
4858         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4859         double const x = _editor->sample_to_pixel (_note[1]);
4860         if (_note[1] > _note[0]) {
4861                 _drag_rect->set_x1 (x);
4862         } else {
4863                 _drag_rect->set_x0 (x);
4864         }
4865 }
4866
4867 void
4868 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4869 {
4870         if (!had_movement) {
4871                 return;
4872         }
4873         
4874         framepos_t const start = min (_note[0], _note[1]);
4875         framecnt_t length = (framecnt_t) fabs (_note[0] - _note[1]);
4876
4877         framecnt_t const g = grid_frames (start);
4878         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4879         
4880         if (_editor->snap_mode() == SnapNormal && length < g) {
4881                 length = g - one_tick;
4882         }
4883
4884         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4885
4886         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4887 }
4888
4889 double
4890 NoteCreateDrag::y_to_region (double y) const
4891 {
4892         double x = 0;
4893         _region_view->get_canvas_group()->canvas_to_item (x, y);
4894         return y;
4895 }
4896
4897 void
4898 NoteCreateDrag::aborted (bool)
4899 {
4900         
4901 }
4902
4903 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4904         : Drag (e, i)
4905         , arv (rv)
4906         , start (start_yn)
4907 {
4908         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
4909 }
4910
4911 void
4912 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4913 {
4914         Drag::start_grab (event, cursor);
4915 }
4916
4917 void
4918 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4919 {
4920         double distance;
4921         double new_length;
4922         framecnt_t len;
4923
4924         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4925
4926         if (start) {
4927                 distance = _drags->current_pointer_x() - grab_x();
4928                 len = ar->fade_in()->back()->when;
4929         } else {
4930                 distance = grab_x() - _drags->current_pointer_x();
4931                 len = ar->fade_out()->back()->when;
4932         }
4933
4934         /* how long should it be ? */
4935
4936         new_length = len + _editor->pixel_to_sample (distance);
4937
4938         /* now check with the region that this is legal */
4939
4940         new_length = ar->verify_xfade_bounds (new_length, start);
4941
4942         if (start) {
4943                 arv->reset_fade_in_shape_width (ar, new_length);
4944         } else {
4945                 arv->reset_fade_out_shape_width (ar, new_length);
4946         }
4947 }
4948
4949 void
4950 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
4951 {
4952         double distance;
4953         double new_length;
4954         framecnt_t len;
4955
4956         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4957
4958         if (start) {
4959                 distance = _drags->current_pointer_x() - grab_x();
4960                 len = ar->fade_in()->back()->when;
4961         } else {
4962                 distance = grab_x() - _drags->current_pointer_x();
4963                 len = ar->fade_out()->back()->when;
4964         }
4965
4966         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
4967         
4968         _editor->begin_reversible_command ("xfade trim");
4969         ar->playlist()->clear_owned_changes (); 
4970
4971         if (start) {
4972                 ar->set_fade_in_length (new_length);
4973         } else {
4974                 ar->set_fade_out_length (new_length);
4975         }
4976
4977         /* Adjusting the xfade may affect other regions in the playlist, so we need
4978            to get undo Commands from the whole playlist rather than just the
4979            region.
4980         */
4981
4982         vector<Command*> cmds;
4983         ar->playlist()->rdiff (cmds);
4984         _editor->session()->add_commands (cmds);
4985         _editor->commit_reversible_command ();
4986
4987 }
4988
4989 void
4990 CrossfadeEdgeDrag::aborted (bool)
4991 {
4992         if (start) {
4993                 arv->redraw_start_xfade ();
4994         } else {
4995                 arv->redraw_end_xfade ();
4996         }
4997 }
4998