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