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