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