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