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