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