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