c95979514ffb85264fe791ffae2cb44aa0dd8baf
[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().order() < b->presentation_info().order();
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() - _real_section->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                 /* cop-out : we really should set the musical position of music-locked tempo sections here, which generally works well.
3344                    The problem is that when the mouse pointer's motion causes the location of the beat to change in a more-or-less
3345                    chaotic way when cross-dragging tempo sections (the beat the pointer is now above could have changed without any pointer motion).
3346                    Until there is a way to deal with this, just pretend the tempo section is audio-locked.
3347                 */
3348                 if (was_music) {
3349                         _real_section->set_position_lock_style (AudioTime);
3350                 }
3351
3352                 map.gui_move_tempo (_real_section, pf);
3353
3354                 if (was_music) {
3355                         _real_section->set_position_lock_style (MusicTime);
3356                 }
3357
3358                 show_verbose_cursor_time (_real_section->frame());
3359         }
3360
3361         _marker->set_position (adjusted_current_frame (event, false));
3362 }
3363
3364 void
3365 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3366 {
3367         if (!_real_section->active()) {
3368                 return;
3369         }
3370         if (!movement_occurred) {
3371                 if (was_double_click()) {
3372                         _editor->edit_tempo_marker (*_marker);
3373                 }
3374                 return;
3375         }
3376
3377         TempoMap& map (_editor->session()->tempo_map());
3378
3379         XMLNode &after = map.get_state();
3380         _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3381         _editor->commit_reversible_command ();
3382
3383         // delete the dummy marker we used for visual representation while moving.
3384         // a new visual marker will show up automatically.
3385         delete _marker;
3386 }
3387
3388 void
3389 TempoMarkerDrag::aborted (bool moved)
3390 {
3391         _marker->set_position (_marker->tempo().frame());
3392         if (moved) {
3393                 TempoMap& map (_editor->session()->tempo_map());
3394                 map.set_state (*before_state, Stateful::current_state_version);
3395                 // delete the dummy marker we used for visual representation while moving.
3396                 // a new visual marker will show up automatically.
3397                 delete _marker;
3398         }
3399 }
3400
3401 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3402         : Drag (e, i)
3403         , _pulse (0.0)
3404         , _tempo (0)
3405         , before_state (0)
3406 {
3407         DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3408
3409 }
3410
3411 void
3412 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3413 {
3414         Drag::start_grab (event, cursor);
3415         TempoMap& map (_editor->session()->tempo_map());
3416         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3417         ostringstream sstr;
3418
3419         sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3420         sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3421         show_verbose_cursor_text (sstr.str());
3422         finished (event, false);
3423 }
3424
3425 void
3426 BBTRulerDrag::setup_pointer_frame_offset ()
3427 {
3428         TempoMap& map (_editor->session()->tempo_map());
3429         const double beat_at_frame = map.beat_at_frame (raw_grab_frame());
3430         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3431         double beat = 0.0;
3432
3433         if (divisions > 0) {
3434                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3435         } else {
3436                 /* while it makes some sense for the user to determine the division to 'grab',
3437                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3438                    and the result over steep tempo curves. Use sixteenths.
3439                 */
3440                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3441         }
3442
3443         _pulse = map.pulse_at_beat (beat);
3444
3445         _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3446
3447 }
3448
3449 void
3450 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3451 {
3452         TempoMap& map (_editor->session()->tempo_map());
3453
3454         if (first_move) {
3455                 /* get current state */
3456                 before_state = &map.get_state();
3457                 _editor->begin_reversible_command (_("dilate tempo"));
3458         }
3459
3460         framepos_t pf;
3461
3462         if (_editor->snap_musical()) {
3463                 pf = adjusted_current_frame (event, false);
3464         } else {
3465                 pf = adjusted_current_frame (event);
3466         }
3467
3468         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier())) {
3469                 /* adjust previous tempo to match pointer frame */
3470                 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3471         }
3472         ostringstream sstr;
3473         sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3474         sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3475         show_verbose_cursor_text (sstr.str());
3476 }
3477
3478 void
3479 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3480 {
3481         if (!movement_occurred) {
3482                 return;
3483         }
3484
3485         TempoMap& map (_editor->session()->tempo_map());
3486
3487         XMLNode &after = map.get_state();
3488         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3489         _editor->commit_reversible_command ();
3490 }
3491
3492 void
3493 BBTRulerDrag::aborted (bool moved)
3494 {
3495         if (moved) {
3496                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3497         }
3498 }
3499
3500
3501 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3502         : Drag (e, &c.track_canvas_item(), false)
3503         , _cursor (c)
3504         , _stop (s)
3505         , _grab_zoom (0.0)
3506 {
3507         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3508 }
3509
3510 /** Do all the things we do when dragging the playhead to make it look as though
3511  *  we have located, without actually doing the locate (because that would cause
3512  *  the diskstream buffers to be refilled, which is too slow).
3513  */
3514 void
3515 CursorDrag::fake_locate (framepos_t t)
3516 {
3517         if (_editor->session () == 0) {
3518                 return;
3519         }
3520
3521         _editor->playhead_cursor->set_position (t);
3522
3523         Session* s = _editor->session ();
3524         if (s->timecode_transmission_suspended ()) {
3525                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3526                 /* This is asynchronous so it will be sent "now"
3527                  */
3528                 s->send_mmc_locate (f);
3529                 /* These are synchronous and will be sent during the next
3530                    process cycle
3531                 */
3532                 s->queue_full_time_code ();
3533                 s->queue_song_position_pointer ();
3534         }
3535
3536         show_verbose_cursor_time (t);
3537         _editor->UpdateAllTransportClocks (t);
3538 }
3539
3540 void
3541 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3542 {
3543         Drag::start_grab (event, c);
3544         setup_snap_delta (_editor->playhead_cursor->current_frame ());
3545
3546         _grab_zoom = _editor->samples_per_pixel;
3547
3548         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3549
3550         _editor->snap_to_with_modifier (where, event);
3551
3552         _editor->_dragging_playhead = true;
3553
3554         Session* s = _editor->session ();
3555
3556         /* grab the track canvas item as well */
3557
3558         _cursor.track_canvas_item().grab();
3559
3560         if (s) {
3561                 if (_was_rolling && _stop) {
3562                         s->request_stop ();
3563                 }
3564
3565                 if (s->is_auditioning()) {
3566                         s->cancel_audition ();
3567                 }
3568
3569
3570                 if (AudioEngine::instance()->connected()) {
3571
3572                         /* do this only if we're the engine is connected
3573                          * because otherwise this request will never be
3574                          * serviced and we'll busy wait forever. likewise,
3575                          * notice if we are disconnected while waiting for the
3576                          * request to be serviced.
3577                          */
3578
3579                         s->request_suspend_timecode_transmission ();
3580                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3581                                 /* twiddle our thumbs */
3582                         }
3583                 }
3584         }
3585
3586         fake_locate (where - snap_delta (event->button.state));
3587 }
3588
3589 void
3590 CursorDrag::motion (GdkEvent* event, bool)
3591 {
3592         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3593         _editor->snap_to_with_modifier (where, event);
3594         if (where != last_pointer_frame()) {
3595                 fake_locate (where - snap_delta (event->button.state));
3596         }
3597 }
3598
3599 void
3600 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3601 {
3602         _editor->_dragging_playhead = false;
3603
3604         _cursor.track_canvas_item().ungrab();
3605
3606         if (!movement_occurred && _stop) {
3607                 return;
3608         }
3609
3610         motion (event, false);
3611
3612         Session* s = _editor->session ();
3613         if (s) {
3614                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3615                 _editor->_pending_locate_request = true;
3616                 s->request_resume_timecode_transmission ();
3617         }
3618 }
3619
3620 void
3621 CursorDrag::aborted (bool)
3622 {
3623         _cursor.track_canvas_item().ungrab();
3624
3625         if (_editor->_dragging_playhead) {
3626                 _editor->session()->request_resume_timecode_transmission ();
3627                 _editor->_dragging_playhead = false;
3628         }
3629
3630         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3631 }
3632
3633 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3634         : RegionDrag (e, i, p, v)
3635 {
3636         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3637 }
3638
3639 void
3640 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3641 {
3642         Drag::start_grab (event, cursor);
3643
3644         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3645         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3646         setup_snap_delta (r->position ());
3647
3648         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3649 }
3650
3651 void
3652 FadeInDrag::setup_pointer_frame_offset ()
3653 {
3654         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3655         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3656         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3657 }
3658
3659 void
3660 FadeInDrag::motion (GdkEvent* event, bool)
3661 {
3662         framecnt_t fade_length;
3663
3664         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3665         _editor->snap_to_with_modifier (pos, event);
3666         pos -= snap_delta (event->button.state);
3667
3668         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3669
3670         if (pos < (region->position() + 64)) {
3671                 fade_length = 64; // this should be a minimum defined somewhere
3672         } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3673                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3674         } else {
3675                 fade_length = pos - region->position();
3676         }
3677
3678         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3679
3680                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3681
3682                 if (!tmp) {
3683                         continue;
3684                 }
3685
3686                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3687         }
3688
3689         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3690 }
3691
3692 void
3693 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3694 {
3695         if (!movement_occurred) {
3696                 return;
3697         }
3698
3699         framecnt_t fade_length;
3700         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3701         _editor->snap_to_with_modifier (pos, event);
3702         pos -= snap_delta (event->button.state);
3703
3704         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3705
3706         if (pos < (region->position() + 64)) {
3707                 fade_length = 64; // this should be a minimum defined somewhere
3708         } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3709                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3710         } else {
3711                 fade_length = pos - region->position();
3712         }
3713
3714         bool in_command = false;
3715
3716         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3717
3718                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3719
3720                 if (!tmp) {
3721                         continue;
3722                 }
3723
3724                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3725                 XMLNode &before = alist->get_state();
3726
3727                 tmp->audio_region()->set_fade_in_length (fade_length);
3728                 tmp->audio_region()->set_fade_in_active (true);
3729
3730                 if (!in_command) {
3731                         _editor->begin_reversible_command (_("change fade in length"));
3732                         in_command = true;
3733                 }
3734                 XMLNode &after = alist->get_state();
3735                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3736         }
3737
3738         if (in_command) {
3739                 _editor->commit_reversible_command ();
3740         }
3741 }
3742
3743 void
3744 FadeInDrag::aborted (bool)
3745 {
3746         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3747                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3748
3749                 if (!tmp) {
3750                         continue;
3751                 }
3752
3753                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3754         }
3755 }
3756
3757 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3758         : RegionDrag (e, i, p, v)
3759 {
3760         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3761 }
3762
3763 void
3764 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3765 {
3766         Drag::start_grab (event, cursor);
3767
3768         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3769         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3770         setup_snap_delta (r->last_frame ());
3771
3772         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3773 }
3774
3775 void
3776 FadeOutDrag::setup_pointer_frame_offset ()
3777 {
3778         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3779         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3780         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3781 }
3782
3783 void
3784 FadeOutDrag::motion (GdkEvent* event, bool)
3785 {
3786         framecnt_t fade_length;
3787
3788         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3789         _editor->snap_to_with_modifier (pos, event);
3790         pos -= snap_delta (event->button.state);
3791
3792         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3793
3794         if (pos > (region->last_frame() - 64)) {
3795                 fade_length = 64; // this should really be a minimum fade defined somewhere
3796         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3797                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3798         } else {
3799                 fade_length = region->last_frame() - pos;
3800         }
3801
3802         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3803
3804                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3805
3806                 if (!tmp) {
3807                         continue;
3808                 }
3809
3810                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3811         }
3812
3813         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3814 }
3815
3816 void
3817 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3818 {
3819         if (!movement_occurred) {
3820                 return;
3821         }
3822
3823         framecnt_t fade_length;
3824
3825         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3826         _editor->snap_to_with_modifier (pos, event);
3827         pos -= snap_delta (event->button.state);
3828
3829         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3830
3831         if (pos > (region->last_frame() - 64)) {
3832                 fade_length = 64; // this should really be a minimum fade defined somewhere
3833         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3834                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3835         } else {
3836                 fade_length = region->last_frame() - pos;
3837         }
3838
3839         bool in_command = false;
3840
3841         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3842
3843                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3844
3845                 if (!tmp) {
3846                         continue;
3847                 }
3848
3849                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3850                 XMLNode &before = alist->get_state();
3851
3852                 tmp->audio_region()->set_fade_out_length (fade_length);
3853                 tmp->audio_region()->set_fade_out_active (true);
3854
3855                 if (!in_command) {
3856                         _editor->begin_reversible_command (_("change fade out length"));
3857                         in_command = true;
3858                 }
3859                 XMLNode &after = alist->get_state();
3860                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3861         }
3862
3863         if (in_command) {
3864                 _editor->commit_reversible_command ();
3865         }
3866 }
3867
3868 void
3869 FadeOutDrag::aborted (bool)
3870 {
3871         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3872                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3873
3874                 if (!tmp) {
3875                         continue;
3876                 }
3877
3878                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3879         }
3880 }
3881
3882 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3883         : Drag (e, i)
3884         , _selection_changed (false)
3885 {
3886         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3887         Gtk::Window* toplevel = _editor->current_toplevel();
3888         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3889
3890         assert (_marker);
3891
3892         _points.push_back (ArdourCanvas::Duple (0, 0));
3893
3894         _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3895 }
3896
3897 MarkerDrag::~MarkerDrag ()
3898 {
3899         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3900                 delete i->location;
3901         }
3902 }
3903
3904 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3905 {
3906         location = new Location (*l);
3907         markers.push_back (m);
3908         move_both = false;
3909 }
3910
3911 void
3912 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3913 {
3914         Drag::start_grab (event, cursor);
3915
3916         bool is_start;
3917
3918         Location *location = _editor->find_location_from_marker (_marker, is_start);
3919         _editor->_dragging_edit_point = true;
3920
3921         update_item (location);
3922
3923         // _drag_line->show();
3924         // _line->raise_to_top();
3925
3926         if (is_start) {
3927                 show_verbose_cursor_time (location->start());
3928         } else {
3929                 show_verbose_cursor_time (location->end());
3930         }
3931         setup_snap_delta (is_start ? location->start() : location->end());
3932
3933         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3934
3935         switch (op) {
3936         case Selection::Toggle:
3937                 /* we toggle on the button release */
3938                 break;
3939         case Selection::Set:
3940                 if (!_editor->selection->selected (_marker)) {
3941                         _editor->selection->set (_marker);
3942                         _selection_changed = true;
3943                 }
3944                 break;
3945         case Selection::Extend:
3946         {
3947                 Locations::LocationList ll;
3948                 list<ArdourMarker*> to_add;
3949                 framepos_t s, e;
3950                 _editor->selection->markers.range (s, e);
3951                 s = min (_marker->position(), s);
3952                 e = max (_marker->position(), e);
3953                 s = min (s, e);
3954                 e = max (s, e);
3955                 if (e < max_framepos) {
3956                         ++e;
3957                 }
3958                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3959                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3960                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3961                         if (lm) {
3962                                 if (lm->start) {
3963                                         to_add.push_back (lm->start);
3964                                 }
3965                                 if (lm->end) {
3966                                         to_add.push_back (lm->end);
3967                                 }
3968                         }
3969                 }
3970                 if (!to_add.empty()) {
3971                         _editor->selection->add (to_add);
3972                         _selection_changed = true;
3973                 }
3974                 break;
3975         }
3976         case Selection::Add:
3977                 _editor->selection->add (_marker);
3978                 _selection_changed = true;
3979
3980                 break;
3981         }
3982
3983         /* Set up copies for us to manipulate during the drag
3984          */
3985
3986         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3987
3988                 Location* l = _editor->find_location_from_marker (*i, is_start);
3989
3990                 if (!l) {
3991                         continue;
3992                 }
3993
3994                 if (l->is_mark()) {
3995                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3996                 } else {
3997                         /* range: check that the other end of the range isn't
3998                            already there.
3999                         */
4000                         CopiedLocationInfo::iterator x;
4001                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4002                                 if (*(*x).location == *l) {
4003                                         break;
4004                                 }
4005                         }
4006                         if (x == _copied_locations.end()) {
4007                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4008                         } else {
4009                                 (*x).markers.push_back (*i);
4010                                 (*x).move_both = true;
4011                         }
4012                 }
4013
4014         }
4015 }
4016
4017 void
4018 MarkerDrag::setup_pointer_frame_offset ()
4019 {
4020         bool is_start;
4021         Location *location = _editor->find_location_from_marker (_marker, is_start);
4022         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4023 }
4024
4025 void
4026 MarkerDrag::motion (GdkEvent* event, bool)
4027 {
4028         framecnt_t f_delta = 0;
4029         bool is_start;
4030         bool move_both = false;
4031         Location *real_location;
4032         Location *copy_location = 0;
4033         framecnt_t const sd = snap_delta (event->button.state);
4034
4035         framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4036         framepos_t next = newframe;
4037
4038         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4039                 move_both = true;
4040         }
4041
4042         CopiedLocationInfo::iterator x;
4043
4044         /* find the marker we're dragging, and compute the delta */
4045
4046         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4047
4048                 copy_location = (*x).location;
4049
4050                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4051
4052                         /* this marker is represented by this
4053                          * CopiedLocationMarkerInfo
4054                          */
4055
4056                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4057                                 /* que pasa ?? */
4058                                 return;
4059                         }
4060
4061                         if (real_location->is_mark()) {
4062                                 f_delta = newframe - copy_location->start();
4063                         } else {
4064
4065
4066                                 switch (_marker->type()) {
4067                                 case ArdourMarker::SessionStart:
4068                                 case ArdourMarker::RangeStart:
4069                                 case ArdourMarker::LoopStart:
4070                                 case ArdourMarker::PunchIn:
4071                                         f_delta = newframe - copy_location->start();
4072                                         break;
4073
4074                                 case ArdourMarker::SessionEnd:
4075                                 case ArdourMarker::RangeEnd:
4076                                 case ArdourMarker::LoopEnd:
4077                                 case ArdourMarker::PunchOut:
4078                                         f_delta = newframe - copy_location->end();
4079                                         break;
4080                                 default:
4081                                         /* what kind of marker is this ? */
4082                                         return;
4083                                 }
4084                         }
4085
4086                         break;
4087                 }
4088         }
4089
4090         if (x == _copied_locations.end()) {
4091                 /* hmm, impossible - we didn't find the dragged marker */
4092                 return;
4093         }
4094
4095         /* now move them all */
4096
4097         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4098
4099                 copy_location = x->location;
4100
4101                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4102                         continue;
4103                 }
4104
4105                 if (real_location->locked()) {
4106                         continue;
4107                 }
4108
4109                 if (copy_location->is_mark()) {
4110
4111                         /* now move it */
4112
4113                         copy_location->set_start (copy_location->start() + f_delta);
4114
4115                 } else {
4116
4117                         framepos_t new_start = copy_location->start() + f_delta;
4118                         framepos_t new_end = copy_location->end() + f_delta;
4119
4120                         if (is_start) { // start-of-range marker
4121
4122                                 if (move_both || (*x).move_both) {
4123                                         copy_location->set_start (new_start);
4124                                         copy_location->set_end (new_end);
4125                                 } else  if (new_start < copy_location->end()) {
4126                                         copy_location->set_start (new_start);
4127                                 } else if (newframe > 0) {
4128                                         //_editor->snap_to (next, RoundUpAlways, true);
4129                                         copy_location->set_end (next);
4130                                         copy_location->set_start (newframe);
4131                                 }
4132
4133                         } else { // end marker
4134
4135                                 if (move_both || (*x).move_both) {
4136                                         copy_location->set_end (new_end);
4137                                         copy_location->set_start (new_start);
4138                                 } else if (new_end > copy_location->start()) {
4139                                         copy_location->set_end (new_end);
4140                                 } else if (newframe > 0) {
4141                                         //_editor->snap_to (next, RoundDownAlways, true);
4142                                         copy_location->set_start (next);
4143                                         copy_location->set_end (newframe);
4144                                 }
4145                         }
4146                 }
4147
4148                 update_item (copy_location);
4149
4150                 /* now lookup the actual GUI items used to display this
4151                  * location and move them to wherever the copy of the location
4152                  * is now. This means that the logic in ARDOUR::Location is
4153                  * still enforced, even though we are not (yet) modifying
4154                  * the real Location itself.
4155                  */
4156
4157                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4158
4159                 if (lm) {
4160                         lm->set_position (copy_location->start(), copy_location->end());
4161                 }
4162
4163         }
4164
4165         assert (!_copied_locations.empty());
4166
4167         show_verbose_cursor_time (newframe);
4168 }
4169
4170 void
4171 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4172 {
4173         if (!movement_occurred) {
4174
4175                 if (was_double_click()) {
4176                         _editor->rename_marker (_marker);
4177                         return;
4178                 }
4179
4180                 /* just a click, do nothing but finish
4181                    off the selection process
4182                 */
4183
4184                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4185                 switch (op) {
4186                 case Selection::Set:
4187                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4188                                 _editor->selection->set (_marker);
4189                                 _selection_changed = true;
4190                         }
4191                         break;
4192
4193                 case Selection::Toggle:
4194                         /* we toggle on the button release, click only */
4195                         _editor->selection->toggle (_marker);
4196                         _selection_changed = true;
4197
4198                         break;
4199
4200                 case Selection::Extend:
4201                 case Selection::Add:
4202                         break;
4203                 }
4204
4205                 if (_selection_changed) {
4206                         _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4207                         _editor->commit_reversible_selection_op();
4208                 }
4209
4210                 return;
4211         }
4212
4213         _editor->_dragging_edit_point = false;
4214
4215         XMLNode &before = _editor->session()->locations()->get_state();
4216         bool in_command = false;
4217
4218         MarkerSelection::iterator i;
4219         CopiedLocationInfo::iterator x;
4220         bool is_start;
4221
4222         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4223              x != _copied_locations.end() && i != _editor->selection->markers.end();
4224              ++i, ++x) {
4225
4226                 Location * location = _editor->find_location_from_marker (*i, is_start);
4227
4228                 if (location) {
4229
4230                         if (location->locked()) {
4231                                 continue;
4232                         }
4233                         if (!in_command) {
4234                                 _editor->begin_reversible_command ( _("move marker") );
4235                                 in_command = true;
4236                         }
4237                         if (location->is_mark()) {
4238                                 location->set_start (((*x).location)->start());
4239                         } else {
4240                                 location->set (((*x).location)->start(), ((*x).location)->end());
4241                         }
4242                 }
4243         }
4244
4245         if (in_command) {
4246                 XMLNode &after = _editor->session()->locations()->get_state();
4247                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4248                 _editor->commit_reversible_command ();
4249         }
4250 }
4251
4252 void
4253 MarkerDrag::aborted (bool movement_occurred)
4254 {
4255         if (!movement_occurred) {
4256                 return;
4257         }
4258
4259         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4260
4261                 /* move all markers to their original location */
4262
4263
4264                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4265
4266                         bool is_start;
4267                         Location * location = _editor->find_location_from_marker (*m, is_start);
4268
4269                         if (location) {
4270                                 (*m)->set_position (is_start ? location->start() : location->end());
4271                         }
4272                 }
4273         }
4274 }
4275
4276 void
4277 MarkerDrag::update_item (Location*)
4278 {
4279         /* noop */
4280 }
4281
4282 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4283         : Drag (e, i)
4284         , _fixed_grab_x (0.0)
4285         , _fixed_grab_y (0.0)
4286         , _cumulative_x_drag (0.0)
4287         , _cumulative_y_drag (0.0)
4288         , _pushing (false)
4289         , _final_index (0)
4290 {
4291         if (_zero_gain_fraction < 0.0) {
4292                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4293         }
4294
4295         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4296
4297         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4298         assert (_point);
4299 }
4300
4301
4302 void
4303 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4304 {
4305         Drag::start_grab (event, _editor->cursors()->fader);
4306
4307         // start the grab at the center of the control point so
4308         // the point doesn't 'jump' to the mouse after the first drag
4309         _fixed_grab_x = _point->get_x();
4310         _fixed_grab_y = _point->get_y();
4311
4312         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4313         setup_snap_delta (pos);
4314
4315         float const fraction = 1 - (_point->get_y() / _point->line().height());
4316         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4317
4318         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4319
4320         if (!_point->can_slide ()) {
4321                 _x_constrained = true;
4322         }
4323 }
4324
4325 void
4326 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4327 {
4328         double dx = _drags->current_pointer_x() - last_pointer_x();
4329         double dy = current_pointer_y() - last_pointer_y();
4330         bool need_snap = true;
4331
4332         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4333                 dx *= 0.1;
4334                 dy *= 0.1;
4335                 need_snap = false;
4336         }
4337
4338         /* coordinate in pixels relative to the start of the region (for region-based automation)
4339            or track (for track-based automation) */
4340         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4341         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4342
4343         // calculate zero crossing point. back off by .01 to stay on the
4344         // positive side of zero
4345         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4346
4347         if (_x_constrained) {
4348                 cx = _fixed_grab_x;
4349         }
4350         if (_y_constrained) {
4351                 cy = _fixed_grab_y;
4352         }
4353
4354         _cumulative_x_drag = cx - _fixed_grab_x;
4355         _cumulative_y_drag = cy - _fixed_grab_y;
4356
4357         cx = max (0.0, cx);
4358         cy = max (0.0, cy);
4359         cy = min ((double) _point->line().height(), cy);
4360
4361         // make sure we hit zero when passing through
4362         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4363                 cy = zero_gain_y;
4364         }
4365
4366         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4367
4368         if (!_x_constrained && need_snap) {
4369                 _editor->snap_to_with_modifier (cx_frames, event);
4370         }
4371
4372         cx_frames -= snap_delta (event->button.state);
4373         cx_frames = min (cx_frames, _point->line().maximum_time());
4374
4375         float const fraction = 1.0 - (cy / _point->line().height());
4376
4377         if (first_motion) {
4378                 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4379                 _editor->begin_reversible_command (_("automation event move"));
4380                 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4381         }
4382         pair<double, float> result;
4383         result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4384
4385         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4386 }
4387
4388 void
4389 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4390 {
4391         if (!movement_occurred) {
4392
4393                 /* just a click */
4394                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4395                         _editor->reset_point_selection ();
4396                 }
4397
4398         } else {
4399                 _point->line().end_drag (_pushing, _final_index);
4400                 _editor->commit_reversible_command ();
4401         }
4402 }
4403
4404 void
4405 ControlPointDrag::aborted (bool)
4406 {
4407         _point->line().reset ();
4408 }
4409
4410 bool
4411 ControlPointDrag::active (Editing::MouseMode m)
4412 {
4413         if (m == Editing::MouseDraw) {
4414                 /* always active in mouse draw */
4415                 return true;
4416         }
4417
4418         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4419         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4420 }
4421
4422 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4423         : Drag (e, i)
4424         , _line (0)
4425         , _fixed_grab_x (0.0)
4426         , _fixed_grab_y (0.0)
4427         , _cumulative_y_drag (0)
4428         , _before (0)
4429         , _after (0)
4430 {
4431         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4432 }
4433
4434 void
4435 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4436 {
4437         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4438         assert (_line);
4439
4440         _item = &_line->grab_item ();
4441
4442         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4443            origin, and ditto for y.
4444         */
4445
4446         double mx = event->button.x;
4447         double my = event->button.y;
4448
4449         _line->grab_item().canvas_to_item (mx, my);
4450
4451         framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4452
4453         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4454                 /* no adjacent points */
4455                 return;
4456         }
4457
4458         Drag::start_grab (event, _editor->cursors()->fader);
4459
4460         /* store grab start in item frame */
4461         double const bx = _line->nth (_before)->get_x();
4462         double const ax = _line->nth (_after)->get_x();
4463         double const click_ratio = (ax - mx) / (ax - bx);
4464
4465         double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4466
4467         _fixed_grab_x = mx;
4468         _fixed_grab_y = cy;
4469
4470         double fraction = 1.0 - (cy / _line->height());
4471
4472         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4473 }
4474
4475 void
4476 LineDrag::motion (GdkEvent* event, bool first_move)
4477 {
4478         double dy = current_pointer_y() - last_pointer_y();
4479
4480         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4481                 dy *= 0.1;
4482         }
4483
4484         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4485
4486         _cumulative_y_drag = cy - _fixed_grab_y;
4487
4488         cy = max (0.0, cy);
4489         cy = min ((double) _line->height(), cy);
4490
4491         double const fraction = 1.0 - (cy / _line->height());
4492         uint32_t ignored;
4493
4494         if (first_move) {
4495                 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4496
4497                 _editor->begin_reversible_command (_("automation range move"));
4498                 _line->start_drag_line (_before, _after, initial_fraction);
4499         }
4500
4501         /* we are ignoring x position for this drag, so we can just pass in anything */
4502         pair<double, float> result;
4503
4504         result = _line->drag_motion (0, fraction, true, false, ignored);
4505         show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4506 }
4507
4508 void
4509 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4510 {
4511         if (movement_occurred) {
4512                 motion (event, false);
4513                 _line->end_drag (false, 0);
4514                 _editor->commit_reversible_command ();
4515         } else {
4516                 /* add a new control point on the line */
4517
4518                 AutomationTimeAxisView* atv;
4519
4520                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4521                         framepos_t where = grab_frame ();
4522
4523                         double cx = 0;
4524                         double cy = _fixed_grab_y;
4525
4526                         _line->grab_item().item_to_canvas (cx, cy);
4527
4528                         atv->add_automation_event (event, where, cy, false);
4529                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4530                         AudioRegionView* arv;
4531
4532                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4533                                 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4534                         }
4535                 }
4536         }
4537 }
4538
4539 void
4540 LineDrag::aborted (bool)
4541 {
4542         _line->reset ();
4543 }
4544
4545 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4546         : Drag (e, i),
4547           _line (0),
4548           _arv (0),
4549           _region_view_grab_x (0.0),
4550           _cumulative_x_drag (0),
4551           _before (0.0),
4552           _max_x (0)
4553 {
4554         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4555 }
4556
4557 void
4558 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4559 {
4560         Drag::start_grab (event);
4561
4562         _line = reinterpret_cast<Line*> (_item);
4563         assert (_line);
4564
4565         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4566
4567         double cx = event->button.x;
4568         double cy = event->button.y;
4569
4570         _item->parent()->canvas_to_item (cx, cy);
4571
4572         /* store grab start in parent frame */
4573         _region_view_grab_x = cx;
4574
4575         _before = *(float*) _item->get_data ("position");
4576
4577         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4578
4579         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4580 }
4581
4582 void
4583 FeatureLineDrag::motion (GdkEvent*, bool)
4584 {
4585         double dx = _drags->current_pointer_x() - last_pointer_x();
4586
4587         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4588
4589         _cumulative_x_drag += dx;
4590
4591         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4592
4593         if (cx > _max_x){
4594                 cx = _max_x;
4595         }
4596         else if(cx < 0){
4597                 cx = 0;
4598         }
4599
4600         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4601         assert (bbox);
4602         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4603
4604         float *pos = new float;
4605         *pos = cx;
4606
4607         _line->set_data ("position", pos);
4608
4609         _before = cx;
4610 }
4611
4612 void
4613 FeatureLineDrag::finished (GdkEvent*, bool)
4614 {
4615         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4616         _arv->update_transient(_before, _before);
4617 }
4618
4619 void
4620 FeatureLineDrag::aborted (bool)
4621 {
4622         //_line->reset ();
4623 }
4624
4625 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4626         : Drag (e, i)
4627         , _vertical_only (false)
4628 {
4629         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4630 }
4631
4632 void
4633 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4634 {
4635         Drag::start_grab (event);
4636         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4637 }
4638
4639 void
4640 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4641 {
4642         framepos_t start;
4643         framepos_t end;
4644         double y1;
4645         double y2;
4646
4647         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4648
4649         framepos_t grab = grab_frame ();
4650         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4651                 _editor->snap_to_with_modifier (grab, event);
4652         } else {
4653                 grab = raw_grab_frame ();
4654         }
4655
4656         /* base start and end on initial click position */
4657
4658         if (pf < grab) {
4659                 start = pf;
4660                 end = grab;
4661         } else {
4662                 end = pf;
4663                 start = grab;
4664         }
4665
4666         if (current_pointer_y() < grab_y()) {
4667                 y1 = current_pointer_y();
4668                 y2 = grab_y();
4669         } else {
4670                 y2 = current_pointer_y();
4671                 y1 = grab_y();
4672         }
4673
4674         if (start != end || y1 != y2) {
4675
4676                 double x1 = _editor->sample_to_pixel (start);
4677                 double x2 = _editor->sample_to_pixel (end);
4678                 const double min_dimension = 2.0;
4679
4680                 if (_vertical_only) {
4681                         /* fixed 10 pixel width */
4682                         x2 = x1 + 10;
4683                 } else {
4684                         if (x2 < x1) {
4685                                 x2 = min (x1 - min_dimension, x2);
4686                         } else {
4687                                 x2 = max (x1 + min_dimension, x2);
4688                         }
4689                 }
4690
4691                 if (y2 < y1) {
4692                         y2 = min (y1 - min_dimension, y2);
4693                 } else {
4694                         y2 = max (y1 + min_dimension, y2);
4695                 }
4696
4697                 /* translate rect into item space and set */
4698
4699                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4700
4701                 /* this drag is a _trackview_only == true drag, so the y1 and
4702                  * y2 (computed using current_pointer_y() and grab_y()) will be
4703                  * relative to the top of the trackview group). The
4704                  * rubberband rect has the same parent/scroll offset as the
4705                  * the trackview group, so we can use the "r" rect directly
4706                  * to set the shape of the rubberband.
4707                  */
4708
4709                 _editor->rubberband_rect->set (r);
4710                 _editor->rubberband_rect->show();
4711                 _editor->rubberband_rect->raise_to_top();
4712
4713                 show_verbose_cursor_time (pf);
4714
4715                 do_select_things (event, true);
4716         }
4717 }
4718
4719 void
4720 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4721 {
4722         framepos_t x1;
4723         framepos_t x2;
4724         framepos_t grab = grab_frame ();
4725         framepos_t lpf = last_pointer_frame ();
4726
4727         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4728                 grab = raw_grab_frame ();
4729                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4730         }
4731
4732         if (grab < lpf) {
4733                 x1 = grab;
4734                 x2 = lpf;
4735         } else {
4736                 x2 = grab;
4737                 x1 = lpf;
4738         }
4739
4740         double y1;
4741         double y2;
4742
4743         if (current_pointer_y() < grab_y()) {
4744                 y1 = current_pointer_y();
4745                 y2 = grab_y();
4746         } else {
4747                 y2 = current_pointer_y();
4748                 y1 = grab_y();
4749         }
4750
4751         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4752 }
4753
4754 void
4755 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4756 {
4757         if (movement_occurred) {
4758
4759                 motion (event, false);
4760                 do_select_things (event, false);
4761
4762         } else {
4763
4764                 /* just a click */
4765
4766                 bool do_deselect = true;
4767                 MidiTimeAxisView* mtv;
4768
4769                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4770                         /* MIDI track */
4771                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4772                                 /* nothing selected */
4773                                 add_midi_region (mtv, true);
4774                                 do_deselect = false;
4775                         }
4776                 }
4777
4778                 /* do not deselect if Primary or Tertiary (toggle-select or
4779                  * extend-select are pressed.
4780                  */
4781
4782                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4783                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4784                     do_deselect) {
4785                         deselect_things ();
4786                 }
4787
4788         }
4789
4790         _editor->rubberband_rect->hide();
4791 }
4792
4793 void
4794 RubberbandSelectDrag::aborted (bool)
4795 {
4796         _editor->rubberband_rect->hide ();
4797 }
4798
4799 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4800         : RegionDrag (e, i, p, v)
4801 {
4802         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4803 }
4804
4805 void
4806 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4807 {
4808         Drag::start_grab (event, cursor);
4809
4810         _editor->get_selection().add (_primary);
4811
4812         framepos_t where = _primary->region()->position();
4813         setup_snap_delta (where);
4814
4815         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4816 }
4817
4818 void
4819 TimeFXDrag::motion (GdkEvent* event, bool)
4820 {
4821         RegionView* rv = _primary;
4822         StreamView* cv = rv->get_time_axis_view().view ();
4823
4824         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4825         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4826         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4827         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4828         _editor->snap_to_with_modifier (pf, event);
4829         pf -= snap_delta (event->button.state);
4830
4831         if (pf > rv->region()->position()) {
4832                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4833         }
4834
4835         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4836 }
4837
4838 void
4839 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4840 {
4841         /* this may have been a single click, no drag. We still want the dialog
4842            to show up in that case, so that the user can manually edit the
4843            parameters for the timestretch.
4844         */
4845
4846         float fraction = 1.0;
4847
4848         if (movement_occurred) {
4849
4850                 motion (event, false);
4851
4852                 _primary->get_time_axis_view().hide_timestretch ();
4853
4854                 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4855
4856                 if (adjusted_frame_pos < _primary->region()->position()) {
4857                         /* backwards drag of the left edge - not usable */
4858                         return;
4859                 }
4860
4861                 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4862
4863                 fraction = (double) newlen / (double) _primary->region()->length();
4864
4865 #ifndef USE_RUBBERBAND
4866                 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4867                 if (_primary->region()->data_type() == DataType::AUDIO) {
4868                         fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4869                 }
4870 #endif
4871         }
4872
4873         if (!_editor->get_selection().regions.empty()) {
4874                 /* primary will already be included in the selection, and edit
4875                    group shared editing will propagate selection across
4876                    equivalent regions, so just use the current region
4877                    selection.
4878                 */
4879
4880                 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4881                         error << _("An error occurred while executing time stretch operation") << endmsg;
4882                 }
4883         }
4884 }
4885
4886 void
4887 TimeFXDrag::aborted (bool)
4888 {
4889         _primary->get_time_axis_view().hide_timestretch ();
4890 }
4891
4892 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4893         : Drag (e, i)
4894 {
4895         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4896 }
4897
4898 void
4899 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4900 {
4901         Drag::start_grab (event);
4902 }
4903
4904 void
4905 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4906 {
4907         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4908 }
4909
4910 void
4911 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4912 {
4913         if (movement_occurred && _editor->session()) {
4914                 /* make sure we stop */
4915                 _editor->session()->request_transport_speed (0.0);
4916         }
4917 }
4918
4919 void
4920 ScrubDrag::aborted (bool)
4921 {
4922         /* XXX: TODO */
4923 }
4924
4925 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4926         : Drag (e, i)
4927         , _operation (o)
4928         , _add (false)
4929         , _time_selection_at_start (!_editor->get_selection().time.empty())
4930 {
4931         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4932
4933         if (_time_selection_at_start) {
4934                 start_at_start = _editor->get_selection().time.start();
4935                 end_at_start = _editor->get_selection().time.end_frame();
4936         }
4937 }
4938
4939 void
4940 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4941 {
4942         if (_editor->session() == 0) {
4943                 return;
4944         }
4945
4946         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4947
4948         switch (_operation) {
4949         case CreateSelection:
4950                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4951                         _add = true;
4952                 } else {
4953                         _add = false;
4954                 }
4955                 cursor = _editor->cursors()->selector;
4956                 Drag::start_grab (event, cursor);
4957                 break;
4958
4959         case SelectionStartTrim:
4960                 if (_editor->clicked_axisview) {
4961                         _editor->clicked_axisview->order_selection_trims (_item, true);
4962                 }
4963                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4964                 break;
4965
4966         case SelectionEndTrim:
4967                 if (_editor->clicked_axisview) {
4968                         _editor->clicked_axisview->order_selection_trims (_item, false);
4969                 }
4970                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4971                 break;
4972
4973         case SelectionMove:
4974                 Drag::start_grab (event, cursor);
4975                 break;
4976
4977         case SelectionExtend:
4978                 Drag::start_grab (event, cursor);
4979                 break;
4980         }
4981
4982         if (_operation == SelectionMove) {
4983                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4984         } else {
4985                 show_verbose_cursor_time (adjusted_current_frame (event));
4986         }
4987 }
4988
4989 void
4990 SelectionDrag::setup_pointer_frame_offset ()
4991 {
4992         switch (_operation) {
4993         case CreateSelection:
4994                 _pointer_frame_offset = 0;
4995                 break;
4996
4997         case SelectionStartTrim:
4998         case SelectionMove:
4999                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5000                 break;
5001
5002         case SelectionEndTrim:
5003                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5004                 break;
5005
5006         case SelectionExtend:
5007                 break;
5008         }
5009 }
5010
5011 void
5012 SelectionDrag::motion (GdkEvent* event, bool first_move)
5013 {
5014         framepos_t start = 0;
5015         framepos_t end = 0;
5016         framecnt_t length = 0;
5017         framecnt_t distance = 0;
5018
5019         framepos_t const pending_position = adjusted_current_frame (event);
5020
5021         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5022                 return;
5023         }
5024
5025         switch (_operation) {
5026         case CreateSelection:
5027         {
5028                 framepos_t grab = grab_frame ();
5029
5030                 if (first_move) {
5031                         grab = adjusted_current_frame (event, false);
5032                         if (grab < pending_position) {
5033                                 _editor->snap_to (grab, RoundDownMaybe);
5034                         }  else {
5035                                 _editor->snap_to (grab, RoundUpMaybe);
5036                         }
5037                 }
5038
5039                 if (pending_position < grab) {
5040                         start = pending_position;
5041                         end = grab;
5042                 } else {
5043                         end = pending_position;
5044                         start = grab;
5045                 }
5046
5047                 /* first drag: Either add to the selection
5048                    or create a new selection
5049                 */
5050
5051                 if (first_move) {
5052
5053                         if (_add) {
5054
5055                                 /* adding to the selection */
5056                                 _editor->set_selected_track_as_side_effect (Selection::Add);
5057                                 _editor->clicked_selection = _editor->selection->add (start, end);
5058                                 _add = false;
5059
5060                         } else {
5061
5062                                 /* new selection */
5063
5064                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5065                                         _editor->set_selected_track_as_side_effect (Selection::Set);
5066                                 }
5067
5068                                 _editor->clicked_selection = _editor->selection->set (start, end);
5069                         }
5070                 }
5071
5072                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5073                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5074                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5075                 if (atest) {
5076                         _editor->selection->add (atest);
5077                         break;
5078                 }
5079
5080                 /* select all tracks within the rectangle that we've marked out so far */
5081                 TrackViewList new_selection;
5082                 TrackViewList& all_tracks (_editor->track_views);
5083
5084                 ArdourCanvas::Coord const top = grab_y();
5085                 ArdourCanvas::Coord const bottom = current_pointer_y();
5086
5087                 if (top >= 0 && bottom >= 0) {
5088
5089                         //first, find the tracks that are covered in the y range selection
5090                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5091                                 if ((*i)->covered_by_y_range (top, bottom)) {
5092                                         new_selection.push_back (*i);
5093                                 }
5094                         }
5095
5096                         //now find any tracks that are GROUPED with the tracks we selected
5097                         TrackViewList grouped_add = new_selection;
5098                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5099                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5100                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5101                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5102                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5103                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5104                                                         grouped_add.push_back (*j);
5105                                         }
5106                                 }
5107                         }
5108
5109                         //now compare our list with the current selection, and add or remove as necessary
5110                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5111                         TrackViewList tracks_to_add;
5112                         TrackViewList tracks_to_remove;
5113                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5114                                 if ( !_editor->selection->tracks.contains ( *i ) )
5115                                         tracks_to_add.push_back ( *i );
5116                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5117                                 if ( !grouped_add.contains ( *i ) )
5118                                         tracks_to_remove.push_back ( *i );
5119                         _editor->selection->add(tracks_to_add);
5120                         _editor->selection->remove(tracks_to_remove);
5121
5122                 }
5123         }
5124         break;
5125
5126         case SelectionStartTrim:
5127
5128                 end = _editor->selection->time[_editor->clicked_selection].end;
5129
5130                 if (pending_position > end) {
5131                         start = end;
5132                 } else {
5133                         start = pending_position;
5134                 }
5135                 break;
5136
5137         case SelectionEndTrim:
5138
5139                 start = _editor->selection->time[_editor->clicked_selection].start;
5140
5141                 if (pending_position < start) {
5142                         end = start;
5143                 } else {
5144                         end = pending_position;
5145                 }
5146
5147                 break;
5148
5149         case SelectionMove:
5150
5151                 start = _editor->selection->time[_editor->clicked_selection].start;
5152                 end = _editor->selection->time[_editor->clicked_selection].end;
5153
5154                 length = end - start;
5155                 distance = pending_position - start;
5156                 start = pending_position;
5157                 _editor->snap_to (start);
5158
5159                 end = start + length;
5160
5161                 break;
5162
5163         case SelectionExtend:
5164                 break;
5165         }
5166
5167         if (start != end) {
5168                 switch (_operation) {
5169                 case SelectionMove:
5170                         if (_time_selection_at_start) {
5171                                 _editor->selection->move_time (distance);
5172                         }
5173                         break;
5174                 default:
5175                         _editor->selection->replace (_editor->clicked_selection, start, end);
5176                 }
5177         }
5178
5179         if (_operation == SelectionMove) {
5180                 show_verbose_cursor_time(start);
5181         } else {
5182                 show_verbose_cursor_time(pending_position);
5183         }
5184 }
5185
5186 void
5187 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5188 {
5189         Session* s = _editor->session();
5190
5191         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5192         if (movement_occurred) {
5193                 motion (event, false);
5194                 /* XXX this is not object-oriented programming at all. ick */
5195                 if (_editor->selection->time.consolidate()) {
5196                         _editor->selection->TimeChanged ();
5197                 }
5198
5199                 /* XXX what if its a music time selection? */
5200                 if (s) {
5201                         if (s->get_play_range() && s->transport_rolling()) {
5202                                 s->request_play_range (&_editor->selection->time, true);
5203                         } else if (!s->config.get_external_sync()) {
5204                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5205                                         if (_operation == SelectionEndTrim)
5206                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5207                                         else
5208                                                 s->request_locate (_editor->get_selection().time.start());
5209                                 }
5210                         }
5211
5212                         if (_editor->get_selection().time.length() != 0) {
5213                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5214                         } else {
5215                                 s->clear_range_selection ();
5216                         }
5217                 }
5218
5219         } else {
5220                 /* just a click, no pointer movement.
5221                  */
5222
5223                 if (_operation == SelectionExtend) {
5224                         if (_time_selection_at_start) {
5225                                 framepos_t pos = adjusted_current_frame (event, false);
5226                                 framepos_t start = min (pos, start_at_start);
5227                                 framepos_t end = max (pos, end_at_start);
5228                                 _editor->selection->set (start, end);
5229                         }
5230                 } else {
5231                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5232                                 if (_editor->clicked_selection) {
5233                                         _editor->selection->remove (_editor->clicked_selection);
5234                                 }
5235                         } else {
5236                                 if (!_editor->clicked_selection) {
5237                                         _editor->selection->clear_time();
5238                                 }
5239                         }
5240                 }
5241
5242                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5243                         _editor->selection->set (_editor->clicked_axisview);
5244                 }
5245
5246                 if (s && s->get_play_range () && s->transport_rolling()) {
5247                         s->request_stop (false, false);
5248                 }
5249
5250         }
5251
5252         _editor->stop_canvas_autoscroll ();
5253         _editor->clicked_selection = 0;
5254         _editor->commit_reversible_selection_op ();
5255 }
5256
5257 void
5258 SelectionDrag::aborted (bool)
5259 {
5260         /* XXX: TODO */
5261 }
5262
5263 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5264         : Drag (e, i, false),
5265           _operation (o),
5266           _copy (false)
5267 {
5268         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5269
5270         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5271                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5272                                                                       physical_screen_height (_editor->current_toplevel()->get_window())));
5273         _drag_rect->hide ();
5274
5275         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5276         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5277 }
5278
5279 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5280 {
5281         /* normal canvas items will be cleaned up when their parent group is deleted. But
5282            this item is created as the child of a long-lived parent group, and so we
5283            need to explicitly delete it.
5284         */
5285         delete _drag_rect;
5286 }
5287
5288 void
5289 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5290 {
5291         if (_editor->session() == 0) {
5292                 return;
5293         }
5294
5295         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5296
5297         if (!_editor->temp_location) {
5298                 _editor->temp_location = new Location (*_editor->session());
5299         }
5300
5301         switch (_operation) {
5302         case CreateSkipMarker:
5303         case CreateRangeMarker:
5304         case CreateTransportMarker:
5305         case CreateCDMarker:
5306
5307                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5308                         _copy = true;
5309                 } else {
5310                         _copy = false;
5311                 }
5312                 cursor = _editor->cursors()->selector;
5313                 break;
5314         }
5315
5316         Drag::start_grab (event, cursor);
5317
5318         show_verbose_cursor_time (adjusted_current_frame (event));
5319 }
5320
5321 void
5322 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5323 {
5324         framepos_t start = 0;
5325         framepos_t end = 0;
5326         ArdourCanvas::Rectangle *crect;
5327
5328         switch (_operation) {
5329         case CreateSkipMarker:
5330                 crect = _editor->range_bar_drag_rect;
5331                 break;
5332         case CreateRangeMarker:
5333                 crect = _editor->range_bar_drag_rect;
5334                 break;
5335         case CreateTransportMarker:
5336                 crect = _editor->transport_bar_drag_rect;
5337                 break;
5338         case CreateCDMarker:
5339                 crect = _editor->cd_marker_bar_drag_rect;
5340                 break;
5341         default:
5342                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5343                 return;
5344                 break;
5345         }
5346
5347         framepos_t const pf = adjusted_current_frame (event);
5348
5349         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5350                 framepos_t grab = grab_frame ();
5351                 _editor->snap_to (grab);
5352
5353                 if (pf < grab_frame()) {
5354                         start = pf;
5355                         end = grab;
5356                 } else {
5357                         end = pf;
5358                         start = grab;
5359                 }
5360
5361                 /* first drag: Either add to the selection
5362                    or create a new selection.
5363                 */
5364
5365                 if (first_move) {
5366
5367                         _editor->temp_location->set (start, end);
5368
5369                         crect->show ();
5370
5371                         update_item (_editor->temp_location);
5372                         _drag_rect->show();
5373                         //_drag_rect->raise_to_top();
5374
5375                 }
5376         }
5377
5378         if (start != end) {
5379                 _editor->temp_location->set (start, end);
5380
5381                 double x1 = _editor->sample_to_pixel (start);
5382                 double x2 = _editor->sample_to_pixel (end);
5383                 crect->set_x0 (x1);
5384                 crect->set_x1 (x2);
5385
5386                 update_item (_editor->temp_location);
5387         }
5388
5389         show_verbose_cursor_time (pf);
5390
5391 }
5392
5393 void
5394 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5395 {
5396         Location * newloc = 0;
5397         string rangename;
5398         int flags;
5399
5400         if (movement_occurred) {
5401                 motion (event, false);
5402                 _drag_rect->hide();
5403
5404                 switch (_operation) {
5405                 case CreateSkipMarker:
5406                 case CreateRangeMarker:
5407                 case CreateCDMarker:
5408                     {
5409                         XMLNode &before = _editor->session()->locations()->get_state();
5410                         if (_operation == CreateSkipMarker) {
5411                                 _editor->begin_reversible_command (_("new skip marker"));
5412                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5413                                 flags = Location::IsRangeMarker | Location::IsSkip;
5414                                 _editor->range_bar_drag_rect->hide();
5415                         } else if (_operation == CreateCDMarker) {
5416                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5417                                 _editor->begin_reversible_command (_("new CD marker"));
5418                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5419                                 _editor->cd_marker_bar_drag_rect->hide();
5420                         } else {
5421                                 _editor->begin_reversible_command (_("new skip marker"));
5422                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5423                                 flags = Location::IsRangeMarker;
5424                                 _editor->range_bar_drag_rect->hide();
5425                         }
5426                         newloc = new Location (
5427                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5428                                 );
5429
5430                         _editor->session()->locations()->add (newloc, true);
5431                         XMLNode &after = _editor->session()->locations()->get_state();
5432                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5433                         _editor->commit_reversible_command ();
5434                         break;
5435                     }
5436
5437                 case CreateTransportMarker:
5438                         // popup menu to pick loop or punch
5439                         _editor->new_transport_marker_context_menu (&event->button, _item);
5440                         break;
5441                 }
5442
5443         } else {
5444
5445                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5446
5447                 if (_operation == CreateTransportMarker) {
5448
5449                         /* didn't drag, so just locate */
5450
5451                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5452
5453                 } else if (_operation == CreateCDMarker) {
5454
5455                         /* didn't drag, but mark is already created so do
5456                          * nothing */
5457
5458                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5459
5460                         framepos_t start;
5461                         framepos_t end;
5462
5463                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5464
5465                         if (end == max_framepos) {
5466                                 end = _editor->session()->current_end_frame ();
5467                         }
5468
5469                         if (start == max_framepos) {
5470                                 start = _editor->session()->current_start_frame ();
5471                         }
5472
5473                         switch (_editor->mouse_mode) {
5474                         case MouseObject:
5475                                 /* find the two markers on either side and then make the selection from it */
5476                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5477                                 break;
5478
5479                         case MouseRange:
5480                                 /* find the two markers on either side of the click and make the range out of it */
5481                                 _editor->selection->set (start, end);
5482                                 break;
5483
5484                         default:
5485                                 break;
5486                         }
5487                 }
5488         }
5489
5490         _editor->stop_canvas_autoscroll ();
5491 }
5492
5493 void
5494 RangeMarkerBarDrag::aborted (bool movement_occurred)
5495 {
5496         if (movement_occurred) {
5497                 _drag_rect->hide ();
5498         }
5499 }
5500
5501 void
5502 RangeMarkerBarDrag::update_item (Location* location)
5503 {
5504         double const x1 = _editor->sample_to_pixel (location->start());
5505         double const x2 = _editor->sample_to_pixel (location->end());
5506
5507         _drag_rect->set_x0 (x1);
5508         _drag_rect->set_x1 (x2);
5509 }
5510
5511 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5512         : Drag (e, i)
5513         , _cumulative_dx (0)
5514         , _cumulative_dy (0)
5515         , _was_selected (false)
5516 {
5517         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5518
5519         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5520         assert (_primary);
5521         _region = &_primary->region_view ();
5522         _note_height = _region->midi_stream_view()->note_height ();
5523 }
5524
5525 void
5526 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5527 {
5528         Drag::start_grab (event);
5529         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5530
5531         if (!(_was_selected = _primary->selected())) {
5532
5533                 /* tertiary-click means extend selection - we'll do that on button release,
5534                    so don't add it here, because otherwise we make it hard to figure
5535                    out the "extend-to" range.
5536                 */
5537
5538                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5539
5540                 if (!extend) {
5541                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5542
5543                         if (add) {
5544                                 _region->note_selected (_primary, true);
5545                         } else {
5546                                 _editor->get_selection().clear_points();
5547                                 _region->unique_select (_primary);
5548                         }
5549                 }
5550         }
5551 }
5552
5553 /** @return Current total drag x change in frames */
5554 frameoffset_t
5555 NoteDrag::total_dx (const guint state) const
5556 {
5557         /* dx in frames */
5558         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5559
5560         /* primary note time */
5561         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5562
5563         /* new time of the primary note in session frames */
5564         frameoffset_t st = n + dx + snap_delta (state);
5565
5566         framepos_t const rp = _region->region()->position ();
5567
5568         /* prevent the note being dragged earlier than the region's position */
5569         st = max (st, rp);
5570
5571         /* possibly snap and return corresponding delta */
5572
5573         bool snap = true;
5574
5575         if (ArdourKeyboard::indicates_snap (state)) {
5576                 if (_editor->snap_mode () != SnapOff) {
5577                         snap = false;
5578                 }
5579         } else {
5580                 if (_editor->snap_mode () == SnapOff) {
5581                         snap = false;
5582                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5583                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5584                                 snap = true;
5585                         }
5586                 }
5587         }
5588
5589         frameoffset_t ret;
5590         if (snap) {
5591                 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5592                 ret =  _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5593         } else {
5594                 ret = st - n - snap_delta (state);
5595         }
5596         return ret;
5597 }
5598
5599 /** @return Current total drag y change in note number */
5600 int8_t
5601 NoteDrag::total_dy () const
5602 {
5603         MidiStreamView* msv = _region->midi_stream_view ();
5604         double const y = _region->midi_view()->y_position ();
5605         /* new current note */
5606         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5607         /* clamp */
5608         n = max (msv->lowest_note(), n);
5609         n = min (msv->highest_note(), n);
5610         /* and work out delta */
5611         return n - msv->y_to_note (grab_y() - y);
5612 }
5613
5614 void
5615 NoteDrag::motion (GdkEvent * event, bool)
5616 {
5617         /* Total change in x and y since the start of the drag */
5618         frameoffset_t const dx = total_dx (event->button.state);
5619         int8_t const dy = total_dy ();
5620
5621         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5622         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5623         double const tdy = -dy * _note_height - _cumulative_dy;
5624
5625         if (tdx || tdy) {
5626                 _cumulative_dx += tdx;
5627                 _cumulative_dy += tdy;
5628
5629                 int8_t note_delta = total_dy();
5630
5631                 _region->move_selection (tdx, tdy, note_delta);
5632
5633                 /* the new note value may be the same as the old one, but we
5634                  * don't know what that means because the selection may have
5635                  * involved more than one note and we might be doing something
5636                  * odd with them. so show the note value anyway, always.
5637                  */
5638
5639                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5640
5641                 _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5642         }
5643 }
5644
5645 void
5646 NoteDrag::finished (GdkEvent* ev, bool moved)
5647 {
5648         if (!moved) {
5649                 /* no motion - select note */
5650
5651                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5652                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5653
5654                         bool changed = false;
5655
5656                         if (_was_selected) {
5657                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5658                                 if (add) {
5659                                         _region->note_deselected (_primary);
5660                                         changed = true;
5661                                 } else {
5662                                         _editor->get_selection().clear_points();
5663                                         _region->unique_select (_primary);
5664                                         changed = true;
5665                                 }
5666                         } else {
5667                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5668                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5669
5670                                 if (!extend && !add && _region->selection_size() > 1) {
5671                                         _editor->get_selection().clear_points();
5672                                         _region->unique_select (_primary);
5673                                         changed = true;
5674                                 } else if (extend) {
5675                                         _region->note_selected (_primary, true, true);
5676                                         changed = true;
5677                                 } else {
5678                                         /* it was added during button press */
5679                                         changed = true;
5680
5681                                 }
5682                         }
5683
5684                         if (changed) {
5685                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5686                                 _editor->commit_reversible_selection_op();
5687                         }
5688                 }
5689         } else {
5690                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5691         }
5692 }
5693
5694 void
5695 NoteDrag::aborted (bool)
5696 {
5697         /* XXX: TODO */
5698 }
5699
5700 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5701 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5702         : Drag (editor, atv->base_item ())
5703         , _ranges (r)
5704         , _y_origin (atv->y_position())
5705         , _nothing_to_drag (false)
5706 {
5707         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5708         setup (atv->lines ());
5709 }
5710
5711 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5712 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5713         : Drag (editor, rv->get_canvas_group ())
5714         , _ranges (r)
5715         , _y_origin (rv->get_time_axis_view().y_position())
5716         , _nothing_to_drag (false)
5717         , _integral (false)
5718 {
5719         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5720
5721         list<boost::shared_ptr<AutomationLine> > lines;
5722
5723         AudioRegionView*      audio_view;
5724         AutomationRegionView* automation_view;
5725         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5726                 lines.push_back (audio_view->get_gain_line ());
5727         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5728                 lines.push_back (automation_view->line ());
5729                 _integral = true;
5730         } else {
5731                 error << _("Automation range drag created for invalid region type") << endmsg;
5732         }
5733
5734         setup (lines);
5735 }
5736
5737 /** @param lines AutomationLines to drag.
5738  *  @param offset Offset from the session start to the points in the AutomationLines.
5739  */
5740 void
5741 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5742 {
5743         /* find the lines that overlap the ranges being dragged */
5744         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5745         while (i != lines.end ()) {
5746                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5747                 ++j;
5748
5749                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5750
5751                 /* check this range against all the AudioRanges that we are using */
5752                 list<AudioRange>::const_iterator k = _ranges.begin ();
5753                 while (k != _ranges.end()) {
5754                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5755                                 break;
5756                         }
5757                         ++k;
5758                 }
5759
5760                 /* add it to our list if it overlaps at all */
5761                 if (k != _ranges.end()) {
5762                         Line n;
5763                         n.line = *i;
5764                         n.state = 0;
5765                         n.range = r;
5766                         _lines.push_back (n);
5767                 }
5768
5769                 i = j;
5770         }
5771
5772         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5773 }
5774
5775 double
5776 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5777 {
5778         return 1.0 - ((global_y - _y_origin) / line->height());
5779 }
5780
5781 double
5782 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5783 {
5784         const double v = list->eval(x);
5785         return _integral ? rint(v) : v;
5786 }
5787
5788 void
5789 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5790 {
5791         Drag::start_grab (event, cursor);
5792
5793         /* Get line states before we start changing things */
5794         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5795                 i->state = &i->line->get_state ();
5796                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5797         }
5798
5799         if (_ranges.empty()) {
5800
5801                 /* No selected time ranges: drag all points */
5802                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5803                         uint32_t const N = i->line->npoints ();
5804                         for (uint32_t j = 0; j < N; ++j) {
5805                                 i->points.push_back (i->line->nth (j));
5806                         }
5807                 }
5808
5809         }
5810
5811         if (_nothing_to_drag) {
5812                 return;
5813         }
5814 }
5815
5816 void
5817 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5818 {
5819         if (_nothing_to_drag && !first_move) {
5820                 return;
5821         }
5822
5823         if (first_move) {
5824                 _editor->begin_reversible_command (_("automation range move"));
5825
5826                 if (!_ranges.empty()) {
5827
5828                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5829
5830                                 framecnt_t const half = (i->start + i->end) / 2;
5831
5832                                 /* find the line that this audio range starts in */
5833                                 list<Line>::iterator j = _lines.begin();
5834                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5835                                         ++j;
5836                                 }
5837
5838                                 if (j != _lines.end()) {
5839                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5840
5841                                 /* j is the line that this audio range starts in; fade into it;
5842                                    64 samples length plucked out of thin air.
5843                                 */
5844
5845                                         framepos_t a = i->start + 64;
5846                                         if (a > half) {
5847                                                 a = half;
5848                                         }
5849
5850                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5851                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5852
5853                                         XMLNode &before = the_list->get_state();
5854                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5855                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5856
5857                                         if (add_p || add_q) {
5858                                                 _editor->session()->add_command (
5859                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5860                                         }
5861                                 }
5862
5863                                 /* same thing for the end */
5864
5865                                 j = _lines.begin();
5866                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5867                                         ++j;
5868                                 }
5869
5870                                 if (j != _lines.end()) {
5871                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5872
5873                                         /* j is the line that this audio range starts in; fade out of it;
5874                                            64 samples length plucked out of thin air.
5875                                         */
5876
5877                                         framepos_t b = i->end - 64;
5878                                         if (b < half) {
5879                                                 b = half;
5880                                         }
5881
5882                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5883                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5884
5885                                         XMLNode &before = the_list->get_state();
5886                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5887                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5888
5889                                         if (add_p || add_q) {
5890                                                 _editor->session()->add_command (
5891                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5892                                         }
5893                                 }
5894                         }
5895
5896                         _nothing_to_drag = true;
5897
5898                         /* Find all the points that should be dragged and put them in the relevant
5899                            points lists in the Line structs.
5900                         */
5901
5902                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5903
5904                                 uint32_t const N = i->line->npoints ();
5905                                 for (uint32_t j = 0; j < N; ++j) {
5906
5907                                         /* here's a control point on this line */
5908                                         ControlPoint* p = i->line->nth (j);
5909                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5910
5911                                         /* see if it's inside a range */
5912                                         list<AudioRange>::const_iterator k = _ranges.begin ();
5913                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5914                                                 ++k;
5915                                         }
5916
5917                                         if (k != _ranges.end()) {
5918                                                 /* dragging this point */
5919                                                 _nothing_to_drag = false;
5920                                                 i->points.push_back (p);
5921                                         }
5922                                 }
5923                         }
5924                 }
5925
5926                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5927                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5928                 }
5929         }
5930
5931         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5932                 float const f = y_fraction (l->line, current_pointer_y());
5933                 /* we are ignoring x position for this drag, so we can just pass in anything */
5934                 pair<double, float> result;
5935                 uint32_t ignored;
5936                 result = l->line->drag_motion (0, f, true, false, ignored);
5937                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5938         }
5939 }
5940
5941 void
5942 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5943 {
5944         if (_nothing_to_drag || !motion_occurred) {
5945                 return;
5946         }
5947
5948         motion (event, false);
5949         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5950                 i->line->end_drag (false, 0);
5951         }
5952
5953         _editor->commit_reversible_command ();
5954 }
5955
5956 void
5957 AutomationRangeDrag::aborted (bool)
5958 {
5959         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5960                 i->line->reset ();
5961         }
5962 }
5963
5964 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5965         : view (v)
5966         , initial_time_axis_view (itav)
5967 {
5968         /* note that time_axis_view may be null if the regionview was created
5969          * as part of a copy operation.
5970          */
5971         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5972         layer = v->region()->layer ();
5973         initial_y = v->get_canvas_group()->position().y;
5974         initial_playlist = v->region()->playlist ();
5975         initial_position = v->region()->position ();
5976         initial_end = v->region()->position () + v->region()->length ();
5977 }
5978
5979 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5980         : Drag (e, i->canvas_item ())
5981         , _region_view (r)
5982         , _patch_change (i)
5983         , _cumulative_dx (0)
5984 {
5985         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5986                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5987                                                    grab_frame()));
5988 }
5989
5990 void
5991 PatchChangeDrag::motion (GdkEvent* ev, bool)
5992 {
5993         framepos_t f = adjusted_current_frame (ev);
5994         boost::shared_ptr<Region> r = _region_view->region ();
5995         f = max (f, r->position ());
5996         f = min (f, r->last_frame ());
5997
5998         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5999         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6000         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6001         _cumulative_dx = dxu;
6002 }
6003
6004 void
6005 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6006 {
6007         if (!movement_occurred) {
6008                 if (was_double_click()) {
6009                         _region_view->edit_patch_change (_patch_change);
6010                 }
6011                 return;
6012         }
6013
6014         boost::shared_ptr<Region> r (_region_view->region ());
6015         framepos_t f = adjusted_current_frame (ev);
6016         f = max (f, r->position ());
6017         f = min (f, r->last_frame ());
6018
6019         _region_view->move_patch_change (
6020                 *_patch_change,
6021                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6022                 );
6023 }
6024
6025 void
6026 PatchChangeDrag::aborted (bool)
6027 {
6028         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6029 }
6030
6031 void
6032 PatchChangeDrag::setup_pointer_frame_offset ()
6033 {
6034         boost::shared_ptr<Region> region = _region_view->region ();
6035         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6036 }
6037
6038 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6039         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6040         , _region_view (rv)
6041 {
6042
6043 }
6044
6045 void
6046 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6047 {
6048         _region_view->update_drag_selection (
6049                 x1, x2, y1, y2,
6050                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6051 }
6052
6053 void
6054 MidiRubberbandSelectDrag::deselect_things ()
6055 {
6056         /* XXX */
6057 }
6058
6059 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6060         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6061         , _region_view (rv)
6062 {
6063         _vertical_only = true;
6064 }
6065
6066 void
6067 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6068 {
6069         double const y = _region_view->midi_view()->y_position ();
6070
6071         y1 = max (0.0, y1 - y);
6072         y2 = max (0.0, y2 - y);
6073
6074         _region_view->update_vertical_drag_selection (
6075                 y1,
6076                 y2,
6077                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6078                 );
6079 }
6080
6081 void
6082 MidiVerticalSelectDrag::deselect_things ()
6083 {
6084         /* XXX */
6085 }
6086
6087 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6088         : RubberbandSelectDrag (e, i)
6089 {
6090
6091 }
6092
6093 void
6094 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6095 {
6096         if (drag_in_progress) {
6097                 /* We just want to select things at the end of the drag, not during it */
6098                 return;
6099         }
6100
6101         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6102
6103         _editor->begin_reversible_selection_op (X_("rubberband selection"));
6104
6105         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6106
6107         _editor->commit_reversible_selection_op ();
6108 }
6109
6110 void
6111 EditorRubberbandSelectDrag::deselect_things ()
6112 {
6113         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6114
6115         _editor->selection->clear_tracks();
6116         _editor->selection->clear_regions();
6117         _editor->selection->clear_points ();
6118         _editor->selection->clear_lines ();
6119         _editor->selection->clear_midi_notes ();
6120
6121         _editor->commit_reversible_selection_op();
6122 }
6123
6124 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6125         : Drag (e, i)
6126         , _region_view (rv)
6127         , _drag_rect (0)
6128 {
6129         _note[0] = _note[1] = 0;
6130 }
6131
6132 NoteCreateDrag::~NoteCreateDrag ()
6133 {
6134         delete _drag_rect;
6135 }
6136
6137 framecnt_t
6138 NoteCreateDrag::grid_frames (framepos_t t) const
6139 {
6140         bool success;
6141         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6142         if (!success) {
6143                 grid_beats = Evoral::Beats(1);
6144         }
6145
6146         return _region_view->region_beats_to_region_frames (grid_beats);
6147 }
6148
6149 void
6150 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6151 {
6152         Drag::start_grab (event, cursor);
6153
6154         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6155
6156         framepos_t pf = _drags->current_pointer_frame ();
6157         framecnt_t const g = grid_frames (pf);
6158
6159         /* Hack so that we always snap to the note that we are over, instead of snapping
6160            to the next one if we're more than halfway through the one we're over.
6161         */
6162         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6163                 pf -= g / 2;
6164         }
6165
6166         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6167         _note[1] = _note[0];
6168
6169         MidiStreamView* sv = _region_view->midi_stream_view ();
6170         double const x = _editor->sample_to_pixel (_note[0]);
6171         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6172
6173         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6174         _drag_rect->set_outline_all ();
6175         _drag_rect->set_outline_color (0xffffff99);
6176         _drag_rect->set_fill_color (0xffffff66);
6177 }
6178
6179 void
6180 NoteCreateDrag::motion (GdkEvent* event, bool)
6181 {
6182         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6183         double const x0 = _editor->sample_to_pixel (_note[0]);
6184         double const x1 = _editor->sample_to_pixel (_note[1]);
6185         _drag_rect->set_x0 (std::min(x0, x1));
6186         _drag_rect->set_x1 (std::max(x0, x1));
6187 }
6188
6189 void
6190 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6191 {
6192         if (!had_movement) {
6193                 return;
6194         }
6195
6196         framepos_t const start = min (_note[0], _note[1]);
6197         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6198
6199         framecnt_t const g = grid_frames (start);
6200         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6201
6202         if (_editor->snap_mode() == SnapNormal && length < g) {
6203                 length = g;
6204         }
6205
6206         Evoral::Beats length_beats = max (
6207                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6208
6209         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6210 }
6211
6212 double
6213 NoteCreateDrag::y_to_region (double y) const
6214 {
6215         double x = 0;
6216         _region_view->get_canvas_group()->canvas_to_item (x, y);
6217         return y;
6218 }
6219
6220 void
6221 NoteCreateDrag::aborted (bool)
6222 {
6223
6224 }
6225
6226 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6227         : Drag (e, i)
6228         , arv (rv)
6229         , start (start_yn)
6230 {
6231         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
6232 }
6233
6234 void
6235 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6236 {
6237         Drag::start_grab (event, cursor);
6238 }
6239
6240 void
6241 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6242 {
6243         double distance;
6244         double new_length;
6245         framecnt_t len;
6246
6247         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6248
6249         if (start) {
6250                 distance = _drags->current_pointer_x() - grab_x();
6251                 len = ar->fade_in()->back()->when;
6252         } else {
6253                 distance = grab_x() - _drags->current_pointer_x();
6254                 len = ar->fade_out()->back()->when;
6255         }
6256
6257         /* how long should it be ? */
6258
6259         new_length = len + _editor->pixel_to_sample (distance);
6260
6261         /* now check with the region that this is legal */
6262
6263         new_length = ar->verify_xfade_bounds (new_length, start);
6264
6265         if (start) {
6266                 arv->reset_fade_in_shape_width (ar, new_length);
6267         } else {
6268                 arv->reset_fade_out_shape_width (ar, new_length);
6269         }
6270 }
6271
6272 void
6273 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6274 {
6275         double distance;
6276         double new_length;
6277         framecnt_t len;
6278
6279         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6280
6281         if (start) {
6282                 distance = _drags->current_pointer_x() - grab_x();
6283                 len = ar->fade_in()->back()->when;
6284         } else {
6285                 distance = grab_x() - _drags->current_pointer_x();
6286                 len = ar->fade_out()->back()->when;
6287         }
6288
6289         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6290
6291         _editor->begin_reversible_command ("xfade trim");
6292         ar->playlist()->clear_owned_changes ();
6293
6294         if (start) {
6295                 ar->set_fade_in_length (new_length);
6296         } else {
6297                 ar->set_fade_out_length (new_length);
6298         }
6299
6300         /* Adjusting the xfade may affect other regions in the playlist, so we need
6301            to get undo Commands from the whole playlist rather than just the
6302            region.
6303         */
6304
6305         vector<Command*> cmds;
6306         ar->playlist()->rdiff (cmds);
6307         _editor->session()->add_commands (cmds);
6308         _editor->commit_reversible_command ();
6309
6310 }
6311
6312 void
6313 CrossfadeEdgeDrag::aborted (bool)
6314 {
6315         if (start) {
6316                 // arv->redraw_start_xfade ();
6317         } else {
6318                 // arv->redraw_end_xfade ();
6319         }
6320 }
6321
6322 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6323         : Drag (e, item, true)
6324         , line (new EditorCursor (*e))
6325 {
6326         line->set_position (pos);
6327         line->show ();
6328 }
6329
6330 RegionCutDrag::~RegionCutDrag ()
6331 {
6332         delete line;
6333 }
6334
6335 void
6336 RegionCutDrag::motion (GdkEvent*, bool)
6337 {
6338         framepos_t where = _drags->current_pointer_frame();
6339         _editor->snap_to (where);
6340
6341         line->set_position (where);
6342 }
6343
6344 void
6345 RegionCutDrag::finished (GdkEvent*, bool)
6346 {
6347         _editor->get_track_canvas()->canvas()->re_enter();
6348
6349         framepos_t pos = _drags->current_pointer_frame();
6350
6351         line->hide ();
6352
6353         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6354
6355         if (rs.empty()) {
6356                 return;
6357         }
6358
6359         _editor->split_regions_at (pos, rs);
6360 }
6361
6362 void
6363 RegionCutDrag::aborted (bool)
6364 {
6365 }