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