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