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