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