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