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