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