52d2c698bd9125e4a7b3e6bc7132e0a43ff889d2
[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);
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)
514 {
515         if (_editor->session()) {
516                 const TempoMap& map (_editor->session()->tempo_map());
517                 framecnt_t pos = grab_frame();
518                 const Meter& m = map.meter_at (pos);
519                 /* not that the frame rate used here can be affected by pull up/down which
520                    might be wrong.
521                 */
522                 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
523                 return view->add_region (grab_frame(), len, true);
524         }
525
526         return boost::shared_ptr<Region>();
527 }
528
529 struct EditorOrderTimeAxisViewSorter {
530         bool operator() (TimeAxisView* a, TimeAxisView* b) {
531                 RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
532                 RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
533                 assert (ra && rb);
534                 return ra->route()->order_key () < rb->route()->order_key ();
535         }
536 };
537
538 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
539         : Drag (e, i)
540         , _primary (p)
541         , _ntracks (0)
542 {
543         _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
544
545         /* Make a list of tracks to refer to during the drag; we include hidden tracks,
546            as some of the regions we are dragging may be on such tracks.
547         */
548
549         TrackViewList track_views = _editor->track_views;
550         track_views.sort (EditorOrderTimeAxisViewSorter ());
551
552         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
553                 _time_axis_views.push_back (*i);
554
555                 TimeAxisView::Children children_list = (*i)->get_child_list ();
556                 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
557                         _time_axis_views.push_back (j->get());
558                 }
559         }
560
561         /* the list of views can be empty at this point if this is a region list-insert drag
562          */
563
564         for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
565                 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
566         }
567
568         RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
569 }
570
571 void
572 RegionDrag::region_going_away (RegionView* v)
573 {
574         list<DraggingView>::iterator i = _views.begin ();
575         while (i != _views.end() && i->view != v) {
576                 ++i;
577         }
578
579         if (i != _views.end()) {
580                 _views.erase (i);
581         }
582 }
583
584 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
585  *  or -1 if it is not found.
586  */
587 int
588 RegionDrag::find_time_axis_view (TimeAxisView* t) const
589 {
590         int i = 0;
591         int const N = _time_axis_views.size ();
592         while (i < N && _time_axis_views[i] != t) {
593                 ++i;
594         }
595
596         if (_time_axis_views[i] != t) {
597                 return -1;
598         }
599
600         return i;
601 }
602
603 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
604         : RegionDrag (e, i, p, v)
605         , _brushing (b)
606         , _ignore_video_lock (false)
607         , _total_x_delta (0)
608         , _last_pointer_time_axis_view (0)
609         , _last_pointer_layer (0)
610         , _ndropzone (0)
611         , _pdropzone (0)
612         , _ddropzone (0)
613 {
614         DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
615 }
616
617 void
618 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
619 {
620         Drag::start_grab (event, cursor);
621         setup_snap_delta (_last_frame_position);
622
623         show_verbose_cursor_time (_last_frame_position);
624
625         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
626         if (tv.first) {
627                 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
628                 assert(_last_pointer_time_axis_view >= 0);
629                 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
630         }
631
632         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
633                 _ignore_video_lock = true;
634         }
635
636         if (_brushing) {
637                 /* cross track dragging seems broken here. disabled for now. */
638                 _y_constrained = true;
639         }
640 }
641
642 double
643 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
644 {
645         /* compute the amount of pointer motion in frames, and where
646            the region would be if we moved it by that much.
647         */
648         *pending_region_position = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
649
650         framepos_t sync_frame;
651         framecnt_t sync_offset;
652         int32_t sync_dir;
653
654         sync_offset = _primary->region()->sync_offset (sync_dir);
655
656         /* we don't handle a sync point that lies before zero.
657          */
658         if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
659
660                 sync_frame = *pending_region_position + (sync_dir * sync_offset);
661
662                 _editor->snap_to_with_modifier (sync_frame, event);
663
664                 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame) - snap_delta (event->button.state);
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, ARDOUR::Normal, 0, 1, region->name());
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>(), ARDOUR::Normal, 0, 1, region->name());
1410                         RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1411                         if (rtav) {
1412                                 rtav->set_height (original->current_height());
1413                         }
1414                         return rtav;
1415                 }
1416         } catch (...) {
1417                 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1418                 return 0;
1419         }
1420 }
1421
1422 void
1423 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1424 {
1425         RegionSelection new_views;
1426         PlaylistSet modified_playlists;
1427         RouteTimeAxisView* new_time_axis_view = 0;
1428
1429         if (_brushing) {
1430                 /* all changes were made during motion event handlers */
1431
1432                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1433                         delete i->view;
1434                 }
1435
1436                 _editor->commit_reversible_command ();
1437                 return;
1438         }
1439
1440         typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1441         PlaylistMapping playlist_mapping;
1442
1443         /* insert the regions into their new playlists */
1444         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1445
1446                 RouteTimeAxisView* dest_rtv = 0;
1447
1448                 if (i->view->region()->locked() || (i->view->region()->video_locked() && !_ignore_video_lock)) {
1449                         continue;
1450                 }
1451
1452                 framepos_t where;
1453
1454                 if (changed_position && !_x_constrained) {
1455                         where = i->view->region()->position() - drag_delta;
1456                 } else {
1457                         where = i->view->region()->position();
1458                 }
1459
1460                 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1461                         /* dragged to drop zone */
1462
1463                         PlaylistMapping::iterator pm;
1464
1465                         if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1466                                 /* first region from this original playlist: create a new track */
1467                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1468                                 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1469                                 dest_rtv = new_time_axis_view;
1470                         } else {
1471                                 /* we already created a new track for regions from this playlist, use it */
1472                                 dest_rtv = pm->second;
1473                         }
1474                 } else {
1475                         /* destination time axis view is the one we dragged to */
1476                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1477                 }
1478
1479                 if (dest_rtv != 0) {
1480                         RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1481                         if (new_view != 0) {
1482                                 new_views.push_back (new_view);
1483                         }
1484                 }
1485
1486                 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1487                    since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1488                  */
1489
1490                 list<DraggingView>::const_iterator next = i;
1491                 ++next;
1492                 delete i->view;
1493                 i = next;
1494         }
1495
1496         /* If we've created new regions either by copying or moving
1497            to a new track, we want to replace the old selection with the new ones
1498         */
1499
1500         if (new_views.size() > 0) {
1501                 _editor->selection->set (new_views);
1502         }
1503
1504         /* write commands for the accumulated diffs for all our modified playlists */
1505         add_stateful_diff_commands_for_playlists (modified_playlists);
1506
1507         _editor->commit_reversible_command ();
1508 }
1509
1510 void
1511 RegionMoveDrag::finished_no_copy (
1512         bool const changed_position,
1513         bool const changed_tracks,
1514         framecnt_t const drag_delta
1515         )
1516 {
1517         RegionSelection new_views;
1518         PlaylistSet modified_playlists;
1519         PlaylistSet frozen_playlists;
1520         set<RouteTimeAxisView*> views_to_update;
1521         RouteTimeAxisView* new_time_axis_view = 0;
1522
1523         typedef map<boost::shared_ptr<Playlist>, RouteTimeAxisView*> PlaylistMapping;
1524         PlaylistMapping playlist_mapping;
1525
1526         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1527
1528                 RegionView* rv = i->view;
1529                 RouteTimeAxisView* dest_rtv = 0;
1530
1531                 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1532                         ++i;
1533                         continue;
1534                 }
1535
1536                 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1537                         /* dragged to drop zone */
1538
1539                         PlaylistMapping::iterator pm;
1540
1541                         if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1542                                 /* first region from this original playlist: create a new track */
1543                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1544                                 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1545                                 dest_rtv = new_time_axis_view;
1546                         } else {
1547                                 /* we already created a new track for regions from this playlist, use it */
1548                                 dest_rtv = pm->second;
1549                         }
1550
1551                 } else {
1552                         /* destination time axis view is the one we dragged to */
1553                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1554                 }
1555
1556                 assert (dest_rtv);
1557
1558                 double const dest_layer = i->layer;
1559
1560                 views_to_update.insert (dest_rtv);
1561
1562                 framepos_t where;
1563
1564                 if (changed_position && !_x_constrained) {
1565                         where = rv->region()->position() - drag_delta;
1566                 } else {
1567                         where = rv->region()->position();
1568                 }
1569
1570                 if (changed_tracks) {
1571
1572                         /* insert into new playlist */
1573
1574                         RegionView* new_view = insert_region_into_playlist (
1575                                 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1576                                 );
1577
1578                         if (new_view == 0) {
1579                                 ++i;
1580                                 continue;
1581                         }
1582
1583                         new_views.push_back (new_view);
1584
1585                         /* remove from old playlist */
1586
1587                         /* the region that used to be in the old playlist is not
1588                            moved to the new one - we use a copy of it. as a result,
1589                            any existing editor for the region should no longer be
1590                            visible.
1591                         */
1592                         rv->hide_region_editor();
1593
1594
1595                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1596
1597                 } else {
1598
1599                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1600
1601                         /* this movement may result in a crossfade being modified, or a layering change,
1602                            so we need to get undo data from the playlist as well as the region.
1603                         */
1604
1605                         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1606                         if (r.second) {
1607                                 playlist->clear_changes ();
1608                         }
1609
1610                         rv->region()->clear_changes ();
1611
1612                         /*
1613                            motion on the same track. plonk the previously reparented region
1614                            back to its original canvas group (its streamview).
1615                            No need to do anything for copies as they are fake regions which will be deleted.
1616                         */
1617
1618                         rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1619                         rv->get_canvas_group()->set_y_position (i->initial_y);
1620                         rv->drag_end ();
1621
1622                         /* just change the model */
1623                         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1624                                 playlist->set_layer (rv->region(), dest_layer);
1625                         }
1626
1627                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1628
1629                         r = frozen_playlists.insert (playlist);
1630
1631                         if (r.second) {
1632                                 playlist->freeze ();
1633                         }
1634
1635                         rv->region()->set_position (where);
1636                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1637                 }
1638
1639                 if (changed_tracks) {
1640
1641                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1642                            was selected in all of them, then removing it from a playlist will have removed all
1643                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1644                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1645                            corresponding regionview, and _views is now empty).
1646
1647                            This could have invalidated any and all iterators into _views.
1648
1649                            The heuristic we use here is: if the region selection is empty, break out of the loop
1650                            here. if the region selection is not empty, then restart the loop because we know that
1651                            we must have removed at least the region(view) we've just been working on as well as any
1652                            that we processed on previous iterations.
1653
1654                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1655                            we can just iterate.
1656                         */
1657
1658
1659                         if (_views.empty()) {
1660                                 break;
1661                         } else {
1662                                 i = _views.begin();
1663                         }
1664
1665                 } else {
1666                         ++i;
1667                 }
1668         }
1669
1670         /* If we've created new regions either by copying or moving
1671            to a new track, we want to replace the old selection with the new ones
1672         */
1673
1674         if (new_views.size() > 0) {
1675                 _editor->selection->set (new_views);
1676         }
1677
1678         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1679                 (*p)->thaw();
1680         }
1681
1682         /* write commands for the accumulated diffs for all our modified playlists */
1683         add_stateful_diff_commands_for_playlists (modified_playlists);
1684         /* applies to _brushing */
1685         _editor->commit_reversible_command ();
1686
1687         /* We have futzed with the layering of canvas items on our streamviews.
1688            If any region changed layer, this will have resulted in the stream
1689            views being asked to set up their region views, and all will be well.
1690            If not, we might now have badly-ordered region views.  Ask the StreamViews
1691            involved to sort themselves out, just in case.
1692         */
1693
1694         for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1695                 (*i)->view()->playlist_layered ((*i)->track ());
1696         }
1697 }
1698
1699 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1700  *  @param region Region to remove.
1701  *  @param playlist playlist To remove from.
1702  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1703  *  that clear_changes () is only called once per playlist.
1704  */
1705 void
1706 RegionMoveDrag::remove_region_from_playlist (
1707         boost::shared_ptr<Region> region,
1708         boost::shared_ptr<Playlist> playlist,
1709         PlaylistSet& modified_playlists
1710         )
1711 {
1712         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1713
1714         if (r.second) {
1715                 playlist->clear_changes ();
1716         }
1717
1718         playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1719 }
1720
1721
1722 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1723  *  clearing the playlist's diff history first if necessary.
1724  *  @param region Region to insert.
1725  *  @param dest_rtv Destination RouteTimeAxisView.
1726  *  @param dest_layer Destination layer.
1727  *  @param where Destination position.
1728  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1729  *  that clear_changes () is only called once per playlist.
1730  *  @return New RegionView, or 0 if no insert was performed.
1731  */
1732 RegionView *
1733 RegionMoveDrag::insert_region_into_playlist (
1734         boost::shared_ptr<Region> region,
1735         RouteTimeAxisView* dest_rtv,
1736         layer_t dest_layer,
1737         framecnt_t where,
1738         PlaylistSet& modified_playlists
1739         )
1740 {
1741         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1742         if (!dest_playlist) {
1743                 return 0;
1744         }
1745
1746         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1747         _new_region_view = 0;
1748         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1749
1750         /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1751         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1752         if (r.second) {
1753                 dest_playlist->clear_changes ();
1754         }
1755
1756         dest_playlist->add_region (region, where);
1757
1758         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1759                 dest_playlist->set_layer (region, dest_layer);
1760         }
1761
1762         c.disconnect ();
1763
1764         assert (_new_region_view);
1765
1766         return _new_region_view;
1767 }
1768
1769 void
1770 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1771 {
1772         _new_region_view = rv;
1773 }
1774
1775 void
1776 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1777 {
1778         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1779                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1780                 if (!c->empty()) {
1781                         _editor->session()->add_command (c);
1782                 } else {
1783                         delete c;
1784                 }
1785         }
1786 }
1787
1788
1789 void
1790 RegionMoveDrag::aborted (bool movement_occurred)
1791 {
1792         if (_copy) {
1793
1794                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1795                         list<DraggingView>::const_iterator next = i;
1796                         ++next;
1797                         delete i->view;
1798                         i = next;
1799                 }
1800
1801                 _views.clear ();
1802
1803         } else {
1804                 RegionMotionDrag::aborted (movement_occurred);
1805         }
1806 }
1807
1808 void
1809 RegionMotionDrag::aborted (bool)
1810 {
1811         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1812
1813                 StreamView* sview = (*i)->view();
1814
1815                 if (sview) {
1816                         if (sview->layer_display() == Expanded) {
1817                                 sview->set_layer_display (Stacked);
1818                         }
1819                 }
1820         }
1821
1822         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1823                 RegionView* rv = i->view;
1824                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1825                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1826                 assert (rtv);
1827                 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1828                 rv->get_canvas_group()->set_y_position (0);
1829                 rv->drag_end ();
1830                 rv->move (-_total_x_delta, 0);
1831                 rv->set_height (rtv->view()->child_height ());
1832         }
1833 }
1834
1835 /** @param b true to brush, otherwise false.
1836  *  @param c true to make copies of the regions being moved, otherwise false.
1837  */
1838 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1839         : RegionMotionDrag (e, i, p, v, b)
1840         , _copy (c)
1841         , _new_region_view (0)
1842 {
1843         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1844
1845         double speed = 1;
1846         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1847         if (rtv && rtv->is_track()) {
1848                 speed = rtv->track()->speed ();
1849         }
1850
1851         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1852 }
1853
1854 void
1855 RegionMoveDrag::setup_pointer_frame_offset ()
1856 {
1857         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1858 }
1859
1860 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1861         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1862 {
1863         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1864
1865         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1866                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1867
1868         _primary = v->view()->create_region_view (r, false, false);
1869
1870         _primary->get_canvas_group()->show ();
1871         _primary->set_position (pos, 0);
1872         _views.push_back (DraggingView (_primary, this, v));
1873
1874         _last_frame_position = pos;
1875
1876         _item = _primary->get_canvas_group ();
1877 }
1878
1879 void
1880 RegionInsertDrag::finished (GdkEvent *, bool)
1881 {
1882         int pos = _views.front().time_axis_view;
1883         assert(pos >= 0 && pos < (int)_time_axis_views.size());
1884
1885         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1886
1887         _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1888         _primary->get_canvas_group()->set_y_position (0);
1889
1890         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1891
1892         _editor->begin_reversible_command (Operations::insert_region);
1893         playlist->clear_changes ();
1894         playlist->add_region (_primary->region (), _last_frame_position);
1895
1896         // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1897         if (Config->get_edit_mode() == Ripple) {
1898                 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1899         }
1900
1901         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1902         _editor->commit_reversible_command ();
1903
1904         delete _primary;
1905         _primary = 0;
1906         _views.clear ();
1907 }
1908
1909 void
1910 RegionInsertDrag::aborted (bool)
1911 {
1912         delete _primary;
1913         _primary = 0;
1914         _views.clear ();
1915 }
1916
1917 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1918         : RegionMoveDrag (e, i, p, v, false, false)
1919 {
1920         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1921 }
1922
1923 struct RegionSelectionByPosition {
1924         bool operator() (RegionView*a, RegionView* b) {
1925                 return a->region()->position () < b->region()->position();
1926         }
1927 };
1928
1929 void
1930 RegionSpliceDrag::motion (GdkEvent* event, bool)
1931 {
1932         /* Which trackview is this ? */
1933
1934         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1935         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1936
1937         /* The region motion is only processed if the pointer is over
1938            an audio track.
1939         */
1940
1941         if (!tv || !tv->is_track()) {
1942                 /* To make sure we hide the verbose canvas cursor when the mouse is
1943                    not held over an audio track.
1944                 */
1945                 _editor->verbose_cursor()->hide ();
1946                 return;
1947         } else {
1948                 _editor->verbose_cursor()->show ();
1949         }
1950
1951         int dir;
1952
1953         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1954                 dir = 1;
1955         } else {
1956                 dir = -1;
1957         }
1958
1959         RegionSelection copy;
1960         _editor->selection->regions.by_position(copy);
1961
1962         framepos_t const pf = adjusted_current_frame (event);
1963
1964         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1965
1966                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1967
1968                 if (!atv) {
1969                         continue;
1970                 }
1971
1972                 boost::shared_ptr<Playlist> playlist;
1973
1974                 if ((playlist = atv->playlist()) == 0) {
1975                         continue;
1976                 }
1977
1978                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1979                         continue;
1980                 }
1981
1982                 if (dir > 0) {
1983                         if (pf < (*i)->region()->last_frame() + 1) {
1984                                 continue;
1985                         }
1986                 } else {
1987                         if (pf > (*i)->region()->first_frame()) {
1988                                 continue;
1989                         }
1990                 }
1991
1992
1993                 playlist->shuffle ((*i)->region(), dir);
1994         }
1995 }
1996
1997 void
1998 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1999 {
2000         RegionMoveDrag::finished (event, movement_occurred);
2001 }
2002
2003 void
2004 RegionSpliceDrag::aborted (bool)
2005 {
2006         /* XXX: TODO */
2007 }
2008
2009 /***
2010  * ripple mode...
2011  */
2012
2013 void
2014 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2015 {
2016
2017         boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2018
2019         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2020         RegionSelection to_ripple;
2021         for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2022                 if ((*i)->position() >= where) {
2023                         to_ripple.push_back (rtv->view()->find_view(*i));
2024                 }
2025         }
2026
2027         for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2028                 if (!exclude.contains (*i)) {
2029                         // the selection has already been added to _views
2030
2031                         if (drag_in_progress) {
2032                                 // do the same things that RegionMotionDrag::motion does when
2033                                 // first_move is true, for the region views that we're adding
2034                                 // to _views this time
2035
2036                                 (*i)->drag_start();
2037                                 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2038                                 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2039                                 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2040                                 rvg->reparent (_editor->_drag_motion_group);
2041
2042                                 // we only need to move in the y direction
2043                                 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2044                                 fudge.x = 0;
2045                                 rvg->move (fudge);
2046
2047                         }
2048                         _views.push_back (DraggingView (*i, this, tav));
2049                 }
2050         }
2051 }
2052
2053 void
2054 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2055 {
2056
2057         for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2058                 // we added all the regions after the selection
2059
2060                 std::list<DraggingView>::iterator to_erase = i++;
2061                 if (!_editor->selection->regions.contains (to_erase->view)) {
2062                         // restore the non-selected regions to their original playlist & positions,
2063                         // and then ripple them back by the length of the regions that were dragged away
2064                         // do the same things as RegionMotionDrag::aborted
2065
2066                         RegionView *rv = to_erase->view;
2067                         TimeAxisView* tv = &(rv->get_time_axis_view ());
2068                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2069                         assert (rtv);
2070
2071                         // plonk them back onto their own track
2072                         rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2073                         rv->get_canvas_group()->set_y_position (0);
2074                         rv->drag_end ();
2075
2076                         if (move_regions) {
2077                                 // move the underlying region to match the view
2078                                 rv->region()->set_position (rv->region()->position() + amount);
2079                         } else {
2080                                 // restore the view to match the underlying region's original position
2081                                 rv->move(-amount, 0);   // second parameter is y delta - seems 0 is OK
2082                         }
2083
2084                         rv->set_height (rtv->view()->child_height ());
2085                         _views.erase (to_erase);
2086                 }
2087         }
2088 }
2089
2090 bool
2091 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2092 {
2093         if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2094                 if (delta_track) {
2095                         return allow_moves_across_tracks;
2096                 } else {
2097                         return true;
2098                 }
2099         }
2100         return false;
2101 }
2102
2103 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2104         : RegionMoveDrag (e, i, p, v, false, false)
2105 {
2106         DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2107         // compute length of selection
2108         RegionSelection selected_regions = _editor->selection->regions;
2109         selection_length = selected_regions.end_frame() - selected_regions.start();
2110
2111         // we'll only allow dragging to another track in ripple mode if all the regions
2112         // being dragged start off on the same track
2113         allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2114         prev_tav = NULL;
2115         prev_amount = 0;
2116         exclude = new RegionList;
2117         for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2118                 exclude->push_back((*i)->region());
2119         }
2120
2121         // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2122         RegionSelection copy;
2123         selected_regions.by_position(copy); // get selected regions sorted by position into copy
2124
2125         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2126         std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2127
2128         for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2129                 // find ripple start point on each applicable playlist
2130                 RegionView *first_selected_on_this_track = NULL;
2131                 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2132                         if ((*i)->region()->playlist() == (*pi)) {
2133                                 // region is on this playlist - it's the first, because they're sorted
2134                                 first_selected_on_this_track = *i;
2135                                 break;
2136                         }
2137                 }
2138                 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2139                 add_all_after_to_views (
2140                                 &first_selected_on_this_track->get_time_axis_view(),
2141                                 first_selected_on_this_track->region()->position(),
2142                                 selected_regions, false);
2143         }
2144
2145         if (allow_moves_across_tracks) {
2146                 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2147         } else {
2148                 orig_tav = NULL;
2149         }
2150
2151 }
2152
2153 void
2154 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2155 {
2156         /* Which trackview is this ? */
2157
2158         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2159         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2160
2161         /* The region motion is only processed if the pointer is over
2162            an audio track.
2163          */
2164
2165         if (!tv || !tv->is_track()) {
2166                 /* To make sure we hide the verbose canvas cursor when the mouse is
2167                    not held over an audiotrack.
2168                  */
2169                 _editor->verbose_cursor()->hide ();
2170                 return;
2171         }
2172
2173         framepos_t where = adjusted_current_frame (event);
2174         assert (where >= 0);
2175         framepos_t after;
2176         double delta = compute_x_delta (event, &after);
2177
2178         framecnt_t amount = _editor->pixel_to_sample (delta);
2179
2180         if (allow_moves_across_tracks) {
2181                 // all the originally selected regions were on the same track
2182
2183                 framecnt_t adjust = 0;
2184                 if (prev_tav && tv != prev_tav) {
2185                         // dragged onto a different track
2186                         // remove the unselected regions from _views, restore them to their original positions
2187                         // and add the regions after the drop point on the new playlist to _views instead.
2188                         // undo the effect of rippling the previous playlist, and include the effect of removing
2189                         // the dragged region(s) from this track
2190
2191                         remove_unselected_from_views (prev_amount, false);
2192                         // ripple previous playlist according to the regions that have been removed onto the new playlist
2193                         prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2194                         prev_amount = 0;
2195
2196                         // move just the selected regions
2197                         RegionMoveDrag::motion(event, first_move);
2198
2199                         // ensure that the ripple operation on the new playlist inserts selection_length time
2200                         adjust = selection_length;
2201                         // ripple the new current playlist
2202                         tv->playlist()->ripple (where, amount+adjust, exclude);
2203
2204                         // add regions after point where drag entered this track to subsequent ripples
2205                         add_all_after_to_views (tv, where, _editor->selection->regions, true);
2206
2207                 } else {
2208                         // motion on same track
2209                         RegionMoveDrag::motion(event, first_move);
2210                 }
2211                 prev_tav = tv;
2212
2213                 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2214                 prev_position = where;
2215         } else {
2216                 // selection encompasses multiple tracks - just drag
2217                 // cross-track drags are forbidden
2218                 RegionMoveDrag::motion(event, first_move);
2219         }
2220
2221         if (!_x_constrained) {
2222                 prev_amount += amount;
2223         }
2224
2225         _last_frame_position = after;
2226 }
2227
2228 void
2229 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2230 {
2231         if (!movement_occurred) {
2232
2233                 /* just a click */
2234
2235                 if (was_double_click() && !_views.empty()) {
2236                         DraggingView dv = _views.front();
2237                         dv.view->show_region_editor ();
2238
2239                 }
2240
2241                 return;
2242         }
2243
2244         _editor->begin_reversible_command(_("Ripple drag"));
2245
2246         // remove the regions being rippled from the dragging view, updating them to
2247         // their new positions
2248         remove_unselected_from_views (prev_amount, true);
2249
2250         if (allow_moves_across_tracks) {
2251                 if (orig_tav) {
2252                         // if regions were dragged across tracks, we've rippled any later
2253                         // regions on the track the regions were dragged off, so we need
2254                         // to add the original track to the undo record
2255                         orig_tav->playlist()->clear_changes();
2256                         vector<Command*> cmds;
2257                         orig_tav->playlist()->rdiff (cmds);
2258                         _editor->session()->add_commands (cmds);
2259                 }
2260                 if (prev_tav && prev_tav != orig_tav) {
2261                         prev_tav->playlist()->clear_changes();
2262                         vector<Command*> cmds;
2263                         prev_tav->playlist()->rdiff (cmds);
2264                         _editor->session()->add_commands (cmds);
2265                 }
2266         } else {
2267                 // selection spanned multiple tracks - all will need adding to undo record
2268
2269                 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2270                 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2271
2272                 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2273                         (*pi)->clear_changes();
2274                         vector<Command*> cmds;
2275                         (*pi)->rdiff (cmds);
2276                         _editor->session()->add_commands (cmds);
2277                 }
2278         }
2279
2280         // other modified playlists are added to undo by RegionMoveDrag::finished()
2281         RegionMoveDrag::finished (event, movement_occurred);
2282         _editor->commit_reversible_command();
2283 }
2284
2285 void
2286 RegionRippleDrag::aborted (bool movement_occurred)
2287 {
2288         RegionMoveDrag::aborted (movement_occurred);
2289         _views.clear ();
2290 }
2291
2292
2293 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2294         : Drag (e, i),
2295           _view (dynamic_cast<MidiTimeAxisView*> (v))
2296 {
2297         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2298
2299         assert (_view);
2300 }
2301
2302 void
2303 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2304 {
2305         if (first_move) {
2306                 _region = add_midi_region (_view);
2307                 _view->playlist()->freeze ();
2308         } else {
2309                 if (_region) {
2310                         framepos_t const f = adjusted_current_frame (event);
2311                         if (f < grab_frame()) {
2312                                 _region->set_position (f);
2313                         }
2314
2315                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2316                            so that if this region is duplicated, its duplicate starts on
2317                            a snap point rather than 1 frame after a snap point.  Otherwise things get
2318                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
2319                            place snapped notes at the start of the region.
2320                         */
2321
2322                         framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2323                         _region->set_length (len < 1 ? 1 : len);
2324                 }
2325         }
2326 }
2327
2328 void
2329 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
2330 {
2331         if (!movement_occurred) {
2332                 add_midi_region (_view);
2333         } else {
2334                 _view->playlist()->thaw ();
2335         }
2336 }
2337
2338 void
2339 RegionCreateDrag::aborted (bool)
2340 {
2341         if (_region) {
2342                 _view->playlist()->thaw ();
2343         }
2344
2345         /* XXX */
2346 }
2347
2348 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2349         : Drag (e, i)
2350         , region (0)
2351         , relative (false)
2352         , at_front (true)
2353         , _was_selected (false)
2354         , _snap_delta (0)
2355 {
2356         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2357 }
2358
2359 void
2360 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2361 {
2362         Gdk::Cursor* cursor;
2363         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2364         assert (cnote);
2365         float x_fraction = cnote->mouse_x_fraction ();
2366
2367         if (x_fraction > 0.0 && x_fraction < 0.25) {
2368                 cursor = _editor->cursors()->left_side_trim;
2369                 at_front = true;
2370         } else  {
2371                 cursor = _editor->cursors()->right_side_trim;
2372                 at_front = false;
2373         }
2374
2375         Drag::start_grab (event, cursor);
2376
2377         region = &cnote->region_view();
2378
2379         double temp;
2380         temp = region->snap_to_pixel (cnote->x0 (), true);
2381         _snap_delta = temp - cnote->x0 ();
2382
2383         _item->grab ();
2384
2385         if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2386                 relative = false;
2387         } else {
2388                 relative = true;
2389         }
2390         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2391         if (ms.size() > 1) {
2392                 /* has to be relative, may make no sense otherwise */
2393                 relative = true;
2394         }
2395
2396         if (!(_was_selected = cnote->selected())) {
2397
2398                 /* tertiary-click means extend selection - we'll do that on button release,
2399                    so don't add it here, because otherwise we make it hard to figure
2400                    out the "extend-to" range.
2401                 */
2402
2403                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2404
2405                 if (!extend) {
2406                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2407
2408                         if (add) {
2409                                 region->note_selected (cnote, true);
2410                         } else {
2411                                 _editor->get_selection().clear_points();
2412                                 region->unique_select (cnote);
2413                         }
2414                 }
2415         }
2416 }
2417
2418 void
2419 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2420 {
2421         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2422         if (first_move) {
2423                 _editor->begin_reversible_command (_("resize notes"));
2424
2425                 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2426                         MidiRegionSelection::iterator next;
2427                         next = r;
2428                         ++next;
2429                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2430                         if (mrv) {
2431                                 mrv->begin_resizing (at_front);
2432                         }
2433                         r = next;
2434                 }
2435         }
2436
2437         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2438                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2439                 assert (nb);
2440                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2441                 if (mrv) {
2442                         double sd = 0.0;
2443                         bool snap = true;
2444                         bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2445
2446                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2447                                 if (_editor->snap_mode () != SnapOff) {
2448                                         snap = false;
2449                                 }
2450                         } else {
2451                                 if (_editor->snap_mode () == SnapOff) {
2452                                         snap = false;
2453                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2454                                         if (apply_snap_delta) {
2455                                                 snap = true;
2456                                         }
2457                                 }
2458                         }
2459
2460                         if (apply_snap_delta) {
2461                                 sd = _snap_delta;
2462                         }
2463
2464                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2465                 }
2466         }
2467 }
2468
2469 void
2470 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2471 {
2472         if (!movement_occurred) {
2473                 /* no motion - select note */
2474                 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2475                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2476                     _editor->current_mouse_mode() == Editing::MouseDraw) {
2477
2478                         bool changed = false;
2479
2480                         if (_was_selected) {
2481                                 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2482                                 if (add) {
2483                                         region->note_deselected (cnote);
2484                                         changed = true;
2485                                 } else {
2486                                         _editor->get_selection().clear_points();
2487                                         region->unique_select (cnote);
2488                                         changed = true;
2489                                 }
2490                         } else {
2491                                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2492                                 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2493
2494                                 if (!extend && !add && region->selection_size() > 1) {
2495                                         _editor->get_selection().clear_points();
2496                                         region->unique_select (cnote);
2497                                         changed = true;
2498                                 } else if (extend) {
2499                                         region->note_selected (cnote, true, true);
2500                                         changed = true;
2501                                 } else {
2502                                         /* it was added during button press */
2503                                         changed = true;
2504                                 }
2505                         }
2506
2507                         if (changed) {
2508                                 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2509                                 _editor->commit_reversible_selection_op();
2510                         }
2511                 }
2512
2513                 return;
2514         }
2515
2516         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2517         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2518                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2519                 assert (nb);
2520                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2521                 double sd = 0.0;
2522                 bool snap = true;
2523                 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2524                 if (mrv) {
2525                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2526                                 if (_editor->snap_mode () != SnapOff) {
2527                                         snap = false;
2528                                 }
2529                         } else {
2530                                 if (_editor->snap_mode () == SnapOff) {
2531                                         snap = false;
2532                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2533                                         if (apply_snap_delta) {
2534                                                 snap = true;
2535                                         }
2536                                 }
2537                         }
2538
2539                         if (apply_snap_delta) {
2540                                 sd = _snap_delta;
2541                         }
2542
2543                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2544                 }
2545         }
2546
2547         _editor->commit_reversible_command ();
2548 }
2549
2550 void
2551 NoteResizeDrag::aborted (bool)
2552 {
2553         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2554         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2555                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2556                 if (mrv) {
2557                         mrv->abort_resizing ();
2558                 }
2559         }
2560 }
2561
2562 AVDraggingView::AVDraggingView (RegionView* v)
2563         : view (v)
2564 {
2565         initial_position = v->region()->position ();
2566 }
2567
2568 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2569         : Drag (e, i)
2570 {
2571         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2572
2573         RegionSelection rs;
2574         TrackViewList empty;
2575         empty.clear();
2576         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2577         std::list<RegionView*> views = rs.by_layer();
2578
2579         _stuck = false;
2580         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2581                 RegionView* rv = (*i);
2582                 if (!rv->region()->video_locked()) {
2583                         continue;
2584                 }
2585                 if (rv->region()->locked()) {
2586                         _stuck = true;
2587                 }
2588                 _views.push_back (AVDraggingView (rv));
2589         }
2590 }
2591
2592 void
2593 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2594 {
2595         Drag::start_grab (event);
2596         if (_editor->session() == 0) {
2597                 return;
2598         }
2599
2600         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2601                 _stuck = false;
2602                 _views.clear();
2603         }
2604
2605         if (_stuck) {
2606                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2607                 return;
2608         }
2609
2610         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2611         _max_backwards_drag = (
2612                           ARDOUR_UI::instance()->video_timeline->get_duration()
2613                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2614                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2615                         );
2616
2617         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2618                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2619                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2620                 }
2621         }
2622         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2623
2624         char buf[128];
2625         Timecode::Time timecode;
2626         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2627         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);
2628         show_verbose_cursor_text (buf);
2629 }
2630
2631 void
2632 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2633 {
2634         if (_editor->session() == 0) {
2635                 return;
2636         }
2637         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2638                 return;
2639         }
2640         if (_stuck) {
2641                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2642                 return;
2643         }
2644
2645         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2646         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2647
2648         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2649                 dt = - _max_backwards_drag;
2650         }
2651
2652         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2653         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2654
2655         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2656                 RegionView* rv = i->view;
2657                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2658                 if (first_move) {
2659                         rv->drag_start ();
2660                         rv->region()->clear_changes ();
2661                         rv->region()->suspend_property_changes();
2662                 }
2663                 rv->region()->set_position(i->initial_position + dt);
2664                 rv->region_changed(ARDOUR::Properties::position);
2665         }
2666
2667         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2668         Timecode::Time timecode;
2669         Timecode::Time timediff;
2670         char buf[128];
2671         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2672         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2673         snprintf (buf, sizeof (buf),
2674                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2675                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2676                         , _("Video Start:"),
2677                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2678                         , _("Diff:"),
2679                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2680                                 );
2681         show_verbose_cursor_text (buf);
2682 }
2683
2684 void
2685 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2686 {
2687         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2688                 return;
2689         }
2690         if (_stuck) {
2691                 return;
2692         }
2693
2694         if (!movement_occurred || ! _editor->session()) {
2695                 return;
2696         }
2697
2698         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2699
2700         _editor->begin_reversible_command (_("Move Video"));
2701
2702         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2703         ARDOUR_UI::instance()->video_timeline->save_undo();
2704         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2705         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2706
2707         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2708                 i->view->drag_end();
2709                 i->view->region()->resume_property_changes ();
2710
2711                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2712         }
2713
2714         _editor->session()->maybe_update_session_range(
2715                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2716                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2717                         );
2718
2719
2720         _editor->commit_reversible_command ();
2721 }
2722
2723 void
2724 VideoTimeLineDrag::aborted (bool)
2725 {
2726         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2727                 return;
2728         }
2729         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2730         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2731
2732         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2733                 i->view->region()->resume_property_changes ();
2734                 i->view->region()->set_position(i->initial_position);
2735         }
2736 }
2737
2738 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2739         : RegionDrag (e, i, p, v)
2740         , _operation (StartTrim)
2741         , _preserve_fade_anchor (preserve_fade_anchor)
2742         , _jump_position_when_done (false)
2743 {
2744         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2745 }
2746
2747 void
2748 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2749 {
2750         double speed = 1.0;
2751         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2752         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2753
2754         if (tv && tv->is_track()) {
2755                 speed = tv->track()->speed();
2756         }
2757
2758         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2759         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2760         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2761
2762         framepos_t const pf = adjusted_current_frame (event);
2763         setup_snap_delta (region_start);
2764
2765         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2766                 /* Move the contents of the region around without changing the region bounds */
2767                 _operation = ContentsTrim;
2768                 Drag::start_grab (event, _editor->cursors()->trimmer);
2769         } else {
2770                 /* These will get overridden for a point trim.*/
2771                 if (pf < (region_start + region_length/2)) {
2772                         /* closer to front */
2773                         _operation = StartTrim;
2774                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2775                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2776                         } else {
2777                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2778                         }
2779                 } else {
2780                         /* closer to end */
2781                         _operation = EndTrim;
2782                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2783                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2784                         } else {
2785                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2786                         }
2787                 }
2788         }
2789         /* jump trim disabled for now
2790         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2791                 _jump_position_when_done = true;
2792         }
2793         */
2794
2795         switch (_operation) {
2796         case StartTrim:
2797                 show_verbose_cursor_time (region_start);
2798                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2799                         i->view->trim_front_starting ();
2800                 }
2801                 break;
2802         case EndTrim:
2803                 show_verbose_cursor_duration (region_start, region_end);
2804                 break;
2805         case ContentsTrim:
2806                 show_verbose_cursor_time (pf);
2807                 break;
2808         }
2809
2810         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2811                 i->view->region()->suspend_property_changes ();
2812         }
2813 }
2814
2815 void
2816 TrimDrag::motion (GdkEvent* event, bool first_move)
2817 {
2818         RegionView* rv = _primary;
2819
2820         double speed = 1.0;
2821         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2822         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2823         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2824         frameoffset_t frame_delta = 0;
2825
2826         if (tv && tv->is_track()) {
2827                 speed = tv->track()->speed();
2828         }
2829         framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2830         framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2831
2832         if (first_move) {
2833
2834                 string trim_type;
2835
2836                 switch (_operation) {
2837                 case StartTrim:
2838                         trim_type = "Region start trim";
2839                         break;
2840                 case EndTrim:
2841                         trim_type = "Region end trim";
2842                         break;
2843                 case ContentsTrim:
2844                         trim_type = "Region content trim";
2845                         break;
2846                 default:
2847                         assert(0);
2848                         break;
2849                 }
2850
2851                 _editor->begin_reversible_command (trim_type);
2852
2853                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2854                         RegionView* rv = i->view;
2855                         rv->enable_display (false);
2856                         rv->region()->playlist()->clear_owned_changes ();
2857
2858                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2859
2860                         if (arv) {
2861                                 arv->temporarily_hide_envelope ();
2862                                 arv->drag_start ();
2863                         }
2864
2865                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2866                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2867
2868                         if (insert_result.second) {
2869                                 pl->freeze();
2870                         }
2871                 }
2872         }
2873
2874         bool non_overlap_trim = false;
2875
2876         if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2877                 non_overlap_trim = true;
2878         }
2879
2880         /* contstrain trim to fade length */
2881         if (_preserve_fade_anchor) {
2882                 switch (_operation) {
2883                         case StartTrim:
2884                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2885                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2886                                         if (!arv) continue;
2887                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2888                                         if (ar->locked()) continue;
2889                                         framecnt_t len = ar->fade_in()->back()->when;
2890                                         if (len < dt) dt = min(dt, len);
2891                                 }
2892                                 break;
2893                         case EndTrim:
2894                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2895                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2896                                         if (!arv) continue;
2897                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2898                                         if (ar->locked()) continue;
2899                                         framecnt_t len = ar->fade_out()->back()->when;
2900                                         if (len < -dt) dt = max(dt, -len);
2901                                 }
2902                                 break;
2903                         case ContentsTrim:
2904                                 break;
2905                 }
2906         }
2907
2908
2909         switch (_operation) {
2910         case StartTrim:
2911                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2912                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2913                         if (changed && _preserve_fade_anchor) {
2914                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2915                                 if (arv) {
2916                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2917                                         framecnt_t len = ar->fade_in()->back()->when;
2918                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2919                                         framepos_t new_length = len - diff;
2920                                         i->anchored_fade_length = min (ar->length(), new_length);
2921                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2922                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2923                                 }
2924                         }
2925                 }
2926                 break;
2927
2928         case EndTrim:
2929                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2930                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2931                         if (changed && _preserve_fade_anchor) {
2932                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2933                                 if (arv) {
2934                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2935                                         framecnt_t len = ar->fade_out()->back()->when;
2936                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2937                                         framepos_t new_length = len + diff;
2938                                         i->anchored_fade_length = min (ar->length(), new_length);
2939                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2940                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2941                                 }
2942                         }
2943                 }
2944                 break;
2945
2946         case ContentsTrim:
2947                 {
2948                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2949
2950                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2951                                 i->view->move_contents (frame_delta);
2952                         }
2953                 }
2954                 break;
2955         }
2956
2957         switch (_operation) {
2958         case StartTrim:
2959                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2960                 break;
2961         case EndTrim:
2962                 show_verbose_cursor_duration ((framepos_t)  rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2963                 break;
2964         case ContentsTrim:
2965                 // show_verbose_cursor_time (frame_delta);
2966                 break;
2967         }
2968 }
2969
2970 void
2971 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2972 {
2973         if (movement_occurred) {
2974                 motion (event, false);
2975
2976                 if (_operation == StartTrim) {
2977                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2978                                 {
2979                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2980                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2981                                            needs to be zero in the undo record.
2982                                         */
2983                                         i->view->trim_front_ending ();
2984                                 }
2985                                 if (_preserve_fade_anchor) {
2986                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2987                                         if (arv) {
2988                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2989                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2990                                                 ar->set_fade_in_length(i->anchored_fade_length);
2991                                                 ar->set_fade_in_active(true);
2992                                         }
2993                                 }
2994                                 if (_jump_position_when_done) {
2995                                         i->view->region()->set_position (i->initial_position);
2996                                 }
2997                         }
2998                 } else if (_operation == EndTrim) {
2999                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3000                                 if (_preserve_fade_anchor) {
3001                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3002                                         if (arv) {
3003                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3004                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3005                                                 ar->set_fade_out_length(i->anchored_fade_length);
3006                                                 ar->set_fade_out_active(true);
3007                                         }
3008                                 }
3009                                 if (_jump_position_when_done) {
3010                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
3011                                 }
3012                         }
3013                 }
3014
3015                 if (!_views.empty()) {
3016                         if (_operation == StartTrim) {
3017                                 _editor->maybe_locate_with_edit_preroll(
3018                                         _views.begin()->view->region()->position());
3019                         }
3020                         if (_operation == EndTrim) {
3021                                 _editor->maybe_locate_with_edit_preroll(
3022                                         _views.begin()->view->region()->position() +
3023                                         _views.begin()->view->region()->length());
3024                         }
3025                 }
3026
3027                 if (!_editor->selection->selected (_primary)) {
3028                         _primary->thaw_after_trim ();
3029                 } else {
3030
3031                         set<boost::shared_ptr<Playlist> > diffed_playlists;
3032
3033                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3034                                 i->view->thaw_after_trim ();
3035                                 i->view->enable_display (true);
3036
3037                                 /* Trimming one region may affect others on the playlist, so we need
3038                                    to get undo Commands from the whole playlist rather than just the
3039                                    region.  Use diffed_playlists to make sure we don't diff a given
3040                                    playlist more than once.
3041                                 */
3042                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3043                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3044                                         vector<Command*> cmds;
3045                                         p->rdiff (cmds);
3046                                         _editor->session()->add_commands (cmds);
3047                                         diffed_playlists.insert (p);
3048                                 }
3049                         }
3050                 }
3051
3052                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3053                         (*p)->thaw ();
3054                 }
3055
3056                 _editor->motion_frozen_playlists.clear ();
3057                 _editor->commit_reversible_command();
3058
3059         } else {
3060                 /* no mouse movement */
3061                 _editor->point_trim (event, adjusted_current_frame (event));
3062         }
3063
3064         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3065                 if (_operation == StartTrim) {
3066                         i->view->trim_front_ending ();
3067                 }
3068
3069                 i->view->region()->resume_property_changes ();
3070         }
3071 }
3072
3073 void
3074 TrimDrag::aborted (bool movement_occurred)
3075 {
3076         /* Our motion method is changing model state, so use the Undo system
3077            to cancel.  Perhaps not ideal, as this will leave an Undo point
3078            behind which may be slightly odd from the user's point of view.
3079         */
3080
3081         finished (0, true);
3082
3083         if (movement_occurred) {
3084                 _editor->undo ();
3085         }
3086
3087         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3088                 i->view->region()->resume_property_changes ();
3089         }
3090 }
3091
3092 void
3093 TrimDrag::setup_pointer_frame_offset ()
3094 {
3095         list<DraggingView>::iterator i = _views.begin ();
3096         while (i != _views.end() && i->view != _primary) {
3097                 ++i;
3098         }
3099
3100         if (i == _views.end()) {
3101                 return;
3102         }
3103
3104         switch (_operation) {
3105         case StartTrim:
3106                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3107                 break;
3108         case EndTrim:
3109                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3110                 break;
3111         case ContentsTrim:
3112                 break;
3113         }
3114 }
3115
3116 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3117         : Drag (e, i),
3118           _copy (c)
3119         , before_state (0)
3120 {
3121         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3122         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3123         assert (_marker);
3124 }
3125
3126 void
3127 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3128 {
3129         Drag::start_grab (event, cursor);
3130         show_verbose_cursor_time (adjusted_current_frame(event));
3131 }
3132
3133 void
3134 MeterMarkerDrag::setup_pointer_frame_offset ()
3135 {
3136         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3137 }
3138
3139 void
3140 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3141 {
3142         if (!_marker->meter().movable()) {
3143                 return;
3144         }
3145
3146         if (first_move) {
3147
3148                 // create a dummy marker for visual representation of moving the
3149                 // section, because whether its a copy or not, we're going to
3150                 // leave or lose the original marker (leave if its a copy; lose if its
3151                 // not, because we'll remove it from the map).
3152
3153                 MeterSection section (_marker->meter());
3154
3155                 if (!section.movable()) {
3156                         return;
3157                 }
3158
3159                 char name[64];
3160                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3161
3162                 _marker = new MeterMarker (
3163                         *_editor,
3164                         *_editor->meter_group,
3165                         UIConfiguration::instance().color ("meter marker"),
3166                         name,
3167                         *new MeterSection (_marker->meter())
3168                 );
3169
3170                 /* use the new marker for the grab */
3171                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3172
3173                 if (!_copy) {
3174                         TempoMap& map (_editor->session()->tempo_map());
3175                         /* get current state */
3176                         before_state = &map.get_state();
3177                         /* remove the section while we drag it */
3178                         map.remove_meter (section, true);
3179                 }
3180         }
3181
3182         framepos_t const pf = adjusted_current_frame (event);
3183
3184         _marker->set_position (pf);
3185         show_verbose_cursor_time (pf);
3186 }
3187
3188 void
3189 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3190 {
3191         if (!movement_occurred) {
3192                 if (was_double_click()) {
3193                         _editor->edit_meter_marker (*_marker);
3194                 }
3195                 return;
3196         }
3197
3198         if (!_marker->meter().movable()) {
3199                 return;
3200         }
3201
3202         motion (event, false);
3203
3204         Timecode::BBT_Time when;
3205
3206         TempoMap& map (_editor->session()->tempo_map());
3207         map.bbt_time (last_pointer_frame(), when);
3208
3209         if (_copy == true) {
3210                 _editor->begin_reversible_command (_("copy meter mark"));
3211                 XMLNode &before = map.get_state();
3212                 map.add_meter (_marker->meter(), when);
3213                 XMLNode &after = map.get_state();
3214                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
3215                 _editor->commit_reversible_command ();
3216
3217         } else {
3218                 _editor->begin_reversible_command (_("move meter mark"));
3219
3220                 /* we removed it before, so add it back now */
3221
3222                 map.add_meter (_marker->meter(), when);
3223                 XMLNode &after = map.get_state();
3224                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3225                 _editor->commit_reversible_command ();
3226         }
3227
3228         // delete the dummy marker we used for visual representation while moving.
3229         // a new visual marker will show up automatically.
3230         delete _marker;
3231 }
3232
3233 void
3234 MeterMarkerDrag::aborted (bool moved)
3235 {
3236         _marker->set_position (_marker->meter().frame ());
3237
3238         if (moved) {
3239                 TempoMap& map (_editor->session()->tempo_map());
3240                 /* we removed it before, so add it back now */
3241                 map.add_meter (_marker->meter(), _marker->meter().frame());
3242                 // delete the dummy marker we used for visual representation while moving.
3243                 // a new visual marker will show up automatically.
3244                 delete _marker;
3245         }
3246 }
3247
3248 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3249         : Drag (e, i)
3250         , _copy (c)
3251         , before_state (0)
3252 {
3253         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3254
3255         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3256         assert (_marker);
3257 }
3258
3259 void
3260 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3261 {
3262         Drag::start_grab (event, cursor);
3263         show_verbose_cursor_time (adjusted_current_frame (event));
3264 }
3265
3266 void
3267 TempoMarkerDrag::setup_pointer_frame_offset ()
3268 {
3269         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
3270 }
3271
3272 void
3273 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3274 {
3275         if (!_marker->tempo().movable()) {
3276                 return;
3277         }
3278
3279         if (first_move) {
3280
3281                 // create a dummy marker for visual representation of moving the
3282                 // section, because whether its a copy or not, we're going to
3283                 // leave or lose the original marker (leave if its a copy; lose if its
3284                 // not, because we'll remove it from the map).
3285
3286                 // create a dummy marker for visual representation of moving the copy.
3287                 // The actual copying is not done before we reach the finish callback.
3288
3289                 char name[64];
3290                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3291
3292                 TempoSection section (_marker->tempo());
3293
3294                 _marker = new TempoMarker (
3295                         *_editor,
3296                         *_editor->tempo_group,
3297                         UIConfiguration::instance().color ("tempo marker"),
3298                         name,
3299                         *new TempoSection (_marker->tempo())
3300                         );
3301
3302                 /* use the new marker for the grab */
3303                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3304
3305                 if (!_copy) {
3306                         TempoMap& map (_editor->session()->tempo_map());
3307                         /* get current state */
3308                         before_state = &map.get_state();
3309                         /* remove the section while we drag it */
3310                         map.remove_tempo (section, true);
3311                 }
3312         }
3313
3314         framepos_t const pf = adjusted_current_frame (event);
3315         _marker->set_position (pf);
3316         show_verbose_cursor_time (pf);
3317 }
3318
3319 void
3320 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3321 {
3322         if (!movement_occurred) {
3323                 if (was_double_click()) {
3324                         _editor->edit_tempo_marker (*_marker);
3325                 }
3326                 return;
3327         }
3328
3329         if (!_marker->tempo().movable()) {
3330                 return;
3331         }
3332
3333         motion (event, false);
3334
3335         TempoMap& map (_editor->session()->tempo_map());
3336         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
3337         Timecode::BBT_Time when;
3338
3339         map.bbt_time (beat_time, when);
3340
3341         if (_copy == true) {
3342                 _editor->begin_reversible_command (_("copy tempo mark"));
3343                 XMLNode &before = map.get_state();
3344                 map.add_tempo (_marker->tempo(), when);
3345                 XMLNode &after = map.get_state();
3346                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
3347                 _editor->commit_reversible_command ();
3348
3349         } else {
3350                 _editor->begin_reversible_command (_("move tempo mark"));
3351                 /* we removed it before, so add it back now */
3352                 map.add_tempo (_marker->tempo(), when);
3353                 XMLNode &after = map.get_state();
3354                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3355                 _editor->commit_reversible_command ();
3356         }
3357
3358         // delete the dummy marker we used for visual representation while moving.
3359         // a new visual marker will show up automatically.
3360         delete _marker;
3361 }
3362
3363 void
3364 TempoMarkerDrag::aborted (bool moved)
3365 {
3366         _marker->set_position (_marker->tempo().frame());
3367         if (moved) {
3368                 TempoMap& map (_editor->session()->tempo_map());
3369                 /* we removed it before, so add it back now */
3370                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
3371                 // delete the dummy marker we used for visual representation while moving.
3372                 // a new visual marker will show up automatically.
3373                 delete _marker;
3374         }
3375 }
3376
3377 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3378         : Drag (e, &c.track_canvas_item(), false)
3379         , _cursor (c)
3380         , _stop (s)
3381         , _grab_zoom (0.0)
3382 {
3383         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3384 }
3385
3386 /** Do all the things we do when dragging the playhead to make it look as though
3387  *  we have located, without actually doing the locate (because that would cause
3388  *  the diskstream buffers to be refilled, which is too slow).
3389  */
3390 void
3391 CursorDrag::fake_locate (framepos_t t)
3392 {
3393         if (_editor->session () == 0) {
3394                 return;
3395         }
3396
3397         _editor->playhead_cursor->set_position (t);
3398
3399         Session* s = _editor->session ();
3400         if (s->timecode_transmission_suspended ()) {
3401                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3402                 /* This is asynchronous so it will be sent "now"
3403                  */
3404                 s->send_mmc_locate (f);
3405                 /* These are synchronous and will be sent during the next
3406                    process cycle
3407                 */
3408                 s->queue_full_time_code ();
3409                 s->queue_song_position_pointer ();
3410         }
3411
3412         show_verbose_cursor_time (t);
3413         _editor->UpdateAllTransportClocks (t);
3414 }
3415
3416 void
3417 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3418 {
3419         Drag::start_grab (event, c);
3420         setup_snap_delta (_editor->playhead_cursor->current_frame ());
3421
3422         _grab_zoom = _editor->samples_per_pixel;
3423
3424         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3425
3426         _editor->snap_to_with_modifier (where, event);
3427
3428         _editor->_dragging_playhead = true;
3429
3430         Session* s = _editor->session ();
3431
3432         /* grab the track canvas item as well */
3433
3434         _cursor.track_canvas_item().grab();
3435
3436         if (s) {
3437                 if (_was_rolling && _stop) {
3438                         s->request_stop ();
3439                 }
3440
3441                 if (s->is_auditioning()) {
3442                         s->cancel_audition ();
3443                 }
3444
3445
3446                 if (AudioEngine::instance()->connected()) {
3447
3448                         /* do this only if we're the engine is connected
3449                          * because otherwise this request will never be
3450                          * serviced and we'll busy wait forever. likewise,
3451                          * notice if we are disconnected while waiting for the
3452                          * request to be serviced.
3453                          */
3454
3455                         s->request_suspend_timecode_transmission ();
3456                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3457                                 /* twiddle our thumbs */
3458                         }
3459                 }
3460         }
3461
3462         fake_locate (where - snap_delta (event->button.state));
3463 }
3464
3465 void
3466 CursorDrag::motion (GdkEvent* event, bool)
3467 {
3468         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3469         _editor->snap_to_with_modifier (where, event);
3470         if (where != last_pointer_frame()) {
3471                 fake_locate (where - snap_delta (event->button.state));
3472         }
3473 }
3474
3475 void
3476 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3477 {
3478         _editor->_dragging_playhead = false;
3479
3480         _cursor.track_canvas_item().ungrab();
3481
3482         if (!movement_occurred && _stop) {
3483                 return;
3484         }
3485
3486         motion (event, false);
3487
3488         Session* s = _editor->session ();
3489         if (s) {
3490                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3491                 _editor->_pending_locate_request = true;
3492                 s->request_resume_timecode_transmission ();
3493         }
3494 }
3495
3496 void
3497 CursorDrag::aborted (bool)
3498 {
3499         _cursor.track_canvas_item().ungrab();
3500
3501         if (_editor->_dragging_playhead) {
3502                 _editor->session()->request_resume_timecode_transmission ();
3503                 _editor->_dragging_playhead = false;
3504         }
3505
3506         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3507 }
3508
3509 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3510         : RegionDrag (e, i, p, v)
3511 {
3512         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3513 }
3514
3515 void
3516 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3517 {
3518         Drag::start_grab (event, cursor);
3519
3520         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3521         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3522         setup_snap_delta (r->position ());
3523
3524         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3525 }
3526
3527 void
3528 FadeInDrag::setup_pointer_frame_offset ()
3529 {
3530         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3531         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3532         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3533 }
3534
3535 void
3536 FadeInDrag::motion (GdkEvent* event, bool)
3537 {
3538         framecnt_t fade_length;
3539
3540         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3541         _editor->snap_to_with_modifier (pos, event);
3542         pos -= snap_delta (event->button.state);
3543
3544         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3545
3546         if (pos < (region->position() + 64)) {
3547                 fade_length = 64; // this should be a minimum defined somewhere
3548         } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3549                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3550         } else {
3551                 fade_length = pos - region->position();
3552         }
3553
3554         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3555
3556                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3557
3558                 if (!tmp) {
3559                         continue;
3560                 }
3561
3562                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3563         }
3564
3565         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3566 }
3567
3568 void
3569 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3570 {
3571         if (!movement_occurred) {
3572                 return;
3573         }
3574
3575         framecnt_t fade_length;
3576         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3577         _editor->snap_to_with_modifier (pos, event);
3578         pos -= snap_delta (event->button.state);
3579
3580         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3581
3582         if (pos < (region->position() + 64)) {
3583                 fade_length = 64; // this should be a minimum defined somewhere
3584         } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3585                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3586         } else {
3587                 fade_length = pos - region->position();
3588         }
3589
3590         bool in_command = false;
3591
3592         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3593
3594                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3595
3596                 if (!tmp) {
3597                         continue;
3598                 }
3599
3600                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3601                 XMLNode &before = alist->get_state();
3602
3603                 tmp->audio_region()->set_fade_in_length (fade_length);
3604                 tmp->audio_region()->set_fade_in_active (true);
3605
3606                 if (!in_command) {
3607                         _editor->begin_reversible_command (_("change fade in length"));
3608                         in_command = true;
3609                 }
3610                 XMLNode &after = alist->get_state();
3611                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3612         }
3613
3614         if (in_command) {
3615                 _editor->commit_reversible_command ();
3616         }
3617 }
3618
3619 void
3620 FadeInDrag::aborted (bool)
3621 {
3622         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3623                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3624
3625                 if (!tmp) {
3626                         continue;
3627                 }
3628
3629                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3630         }
3631 }
3632
3633 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3634         : RegionDrag (e, i, p, v)
3635 {
3636         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3637 }
3638
3639 void
3640 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3641 {
3642         Drag::start_grab (event, cursor);
3643
3644         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3645         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3646         setup_snap_delta (r->last_frame ());
3647
3648         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3649 }
3650
3651 void
3652 FadeOutDrag::setup_pointer_frame_offset ()
3653 {
3654         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3655         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3656         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3657 }
3658
3659 void
3660 FadeOutDrag::motion (GdkEvent* event, bool)
3661 {
3662         framecnt_t fade_length;
3663
3664         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3665         _editor->snap_to_with_modifier (pos, event);
3666         pos -= snap_delta (event->button.state);
3667
3668         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3669
3670         if (pos > (region->last_frame() - 64)) {
3671                 fade_length = 64; // this should really be a minimum fade defined somewhere
3672         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3673                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3674         } else {
3675                 fade_length = region->last_frame() - pos;
3676         }
3677
3678         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3679
3680                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3681
3682                 if (!tmp) {
3683                         continue;
3684                 }
3685
3686                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3687         }
3688
3689         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3690 }
3691
3692 void
3693 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3694 {
3695         if (!movement_occurred) {
3696                 return;
3697         }
3698
3699         framecnt_t fade_length;
3700
3701         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3702         _editor->snap_to_with_modifier (pos, event);
3703         pos -= snap_delta (event->button.state);
3704
3705         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3706
3707         if (pos > (region->last_frame() - 64)) {
3708                 fade_length = 64; // this should really be a minimum fade defined somewhere
3709         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3710                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3711         } else {
3712                 fade_length = region->last_frame() - pos;
3713         }
3714
3715         bool in_command = false;
3716
3717         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3718
3719                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3720
3721                 if (!tmp) {
3722                         continue;
3723                 }
3724
3725                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3726                 XMLNode &before = alist->get_state();
3727
3728                 tmp->audio_region()->set_fade_out_length (fade_length);
3729                 tmp->audio_region()->set_fade_out_active (true);
3730
3731                 if (!in_command) {
3732                         _editor->begin_reversible_command (_("change fade out length"));
3733                         in_command = true;
3734                 }
3735                 XMLNode &after = alist->get_state();
3736                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3737         }
3738
3739         if (in_command) {
3740                 _editor->commit_reversible_command ();
3741         }
3742 }
3743
3744 void
3745 FadeOutDrag::aborted (bool)
3746 {
3747         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3748                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3749
3750                 if (!tmp) {
3751                         continue;
3752                 }
3753
3754                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3755         }
3756 }
3757
3758 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3759         : Drag (e, i)
3760         , _selection_changed (false)
3761 {
3762         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3763
3764         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3765         assert (_marker);
3766
3767         _points.push_back (ArdourCanvas::Duple (0, 0));
3768         _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3769 }
3770
3771 MarkerDrag::~MarkerDrag ()
3772 {
3773         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3774                 delete i->location;
3775         }
3776 }
3777
3778 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3779 {
3780         location = new Location (*l);
3781         markers.push_back (m);
3782         move_both = false;
3783 }
3784
3785 void
3786 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3787 {
3788         Drag::start_grab (event, cursor);
3789
3790         bool is_start;
3791
3792         Location *location = _editor->find_location_from_marker (_marker, is_start);
3793         _editor->_dragging_edit_point = true;
3794
3795         update_item (location);
3796
3797         // _drag_line->show();
3798         // _line->raise_to_top();
3799
3800         if (is_start) {
3801                 show_verbose_cursor_time (location->start());
3802         } else {
3803                 show_verbose_cursor_time (location->end());
3804         }
3805         setup_snap_delta (is_start ? location->start() : location->end());
3806
3807         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3808
3809         switch (op) {
3810         case Selection::Toggle:
3811                 /* we toggle on the button release */
3812                 break;
3813         case Selection::Set:
3814                 if (!_editor->selection->selected (_marker)) {
3815                         _editor->selection->set (_marker);
3816                         _selection_changed = true;
3817                 }
3818                 break;
3819         case Selection::Extend:
3820         {
3821                 Locations::LocationList ll;
3822                 list<ArdourMarker*> to_add;
3823                 framepos_t s, e;
3824                 _editor->selection->markers.range (s, e);
3825                 s = min (_marker->position(), s);
3826                 e = max (_marker->position(), e);
3827                 s = min (s, e);
3828                 e = max (s, e);
3829                 if (e < max_framepos) {
3830                         ++e;
3831                 }
3832                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3833                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3834                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3835                         if (lm) {
3836                                 if (lm->start) {
3837                                         to_add.push_back (lm->start);
3838                                 }
3839                                 if (lm->end) {
3840                                         to_add.push_back (lm->end);
3841                                 }
3842                         }
3843                 }
3844                 if (!to_add.empty()) {
3845                         _editor->selection->add (to_add);
3846                         _selection_changed = true;
3847                 }
3848                 break;
3849         }
3850         case Selection::Add:
3851                 _editor->selection->add (_marker);
3852                 _selection_changed = true;
3853
3854                 break;
3855         }
3856
3857         /* Set up copies for us to manipulate during the drag
3858          */
3859
3860         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3861
3862                 Location* l = _editor->find_location_from_marker (*i, is_start);
3863
3864                 if (!l) {
3865                         continue;
3866                 }
3867
3868                 if (l->is_mark()) {
3869                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3870                 } else {
3871                         /* range: check that the other end of the range isn't
3872                            already there.
3873                         */
3874                         CopiedLocationInfo::iterator x;
3875                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3876                                 if (*(*x).location == *l) {
3877                                         break;
3878                                 }
3879                         }
3880                         if (x == _copied_locations.end()) {
3881                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3882                         } else {
3883                                 (*x).markers.push_back (*i);
3884                                 (*x).move_both = true;
3885                         }
3886                 }
3887
3888         }
3889 }
3890
3891 void
3892 MarkerDrag::setup_pointer_frame_offset ()
3893 {
3894         bool is_start;
3895         Location *location = _editor->find_location_from_marker (_marker, is_start);
3896         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3897 }
3898
3899 void
3900 MarkerDrag::motion (GdkEvent* event, bool)
3901 {
3902         framecnt_t f_delta = 0;
3903         bool is_start;
3904         bool move_both = false;
3905         Location *real_location;
3906         Location *copy_location = 0;
3907         framecnt_t const sd = snap_delta (event->button.state);
3908
3909         framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
3910         framepos_t next = newframe;
3911
3912         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
3913                 move_both = true;
3914         }
3915
3916         CopiedLocationInfo::iterator x;
3917
3918         /* find the marker we're dragging, and compute the delta */
3919
3920         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3921
3922                 copy_location = (*x).location;
3923
3924                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3925
3926                         /* this marker is represented by this
3927                          * CopiedLocationMarkerInfo
3928                          */
3929
3930                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3931                                 /* que pasa ?? */
3932                                 return;
3933                         }
3934
3935                         if (real_location->is_mark()) {
3936                                 f_delta = newframe - copy_location->start();
3937                         } else {
3938
3939
3940                                 switch (_marker->type()) {
3941                                 case ArdourMarker::SessionStart:
3942                                 case ArdourMarker::RangeStart:
3943                                 case ArdourMarker::LoopStart:
3944                                 case ArdourMarker::PunchIn:
3945                                         f_delta = newframe - copy_location->start();
3946                                         break;
3947
3948                                 case ArdourMarker::SessionEnd:
3949                                 case ArdourMarker::RangeEnd:
3950                                 case ArdourMarker::LoopEnd:
3951                                 case ArdourMarker::PunchOut:
3952                                         f_delta = newframe - copy_location->end();
3953                                         break;
3954                                 default:
3955                                         /* what kind of marker is this ? */
3956                                         return;
3957                                 }
3958                         }
3959
3960                         break;
3961                 }
3962         }
3963
3964         if (x == _copied_locations.end()) {
3965                 /* hmm, impossible - we didn't find the dragged marker */
3966                 return;
3967         }
3968
3969         /* now move them all */
3970
3971         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3972
3973                 copy_location = x->location;
3974
3975                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3976                         continue;
3977                 }
3978
3979                 if (real_location->locked()) {
3980                         continue;
3981                 }
3982
3983                 if (copy_location->is_mark()) {
3984
3985                         /* now move it */
3986
3987                         copy_location->set_start (copy_location->start() + f_delta);
3988
3989                 } else {
3990
3991                         framepos_t new_start = copy_location->start() + f_delta;
3992                         framepos_t new_end = copy_location->end() + f_delta;
3993
3994                         if (is_start) { // start-of-range marker
3995
3996                                 if (move_both || (*x).move_both) {
3997                                         copy_location->set_start (new_start);
3998                                         copy_location->set_end (new_end);
3999                                 } else  if (new_start < copy_location->end()) {
4000                                         copy_location->set_start (new_start);
4001                                 } else if (newframe > 0) {
4002                                         //_editor->snap_to (next, RoundUpAlways, true);
4003                                         copy_location->set_end (next);
4004                                         copy_location->set_start (newframe);
4005                                 }
4006
4007                         } else { // end marker
4008
4009                                 if (move_both || (*x).move_both) {
4010                                         copy_location->set_end (new_end);
4011                                         copy_location->set_start (new_start);
4012                                 } else if (new_end > copy_location->start()) {
4013                                         copy_location->set_end (new_end);
4014                                 } else if (newframe > 0) {
4015                                         //_editor->snap_to (next, RoundDownAlways, true);
4016                                         copy_location->set_start (next);
4017                                         copy_location->set_end (newframe);
4018                                 }
4019                         }
4020                 }
4021
4022                 update_item (copy_location);
4023
4024                 /* now lookup the actual GUI items used to display this
4025                  * location and move them to wherever the copy of the location
4026                  * is now. This means that the logic in ARDOUR::Location is
4027                  * still enforced, even though we are not (yet) modifying
4028                  * the real Location itself.
4029                  */
4030
4031                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4032
4033                 if (lm) {
4034                         lm->set_position (copy_location->start(), copy_location->end());
4035                 }
4036
4037         }
4038
4039         assert (!_copied_locations.empty());
4040
4041         show_verbose_cursor_time (newframe);
4042 }
4043
4044 void
4045 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4046 {
4047         if (!movement_occurred) {
4048
4049                 if (was_double_click()) {
4050                         _editor->rename_marker (_marker);
4051                         return;
4052                 }
4053
4054                 /* just a click, do nothing but finish
4055                    off the selection process
4056                 */
4057
4058                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4059                 switch (op) {
4060                 case Selection::Set:
4061                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4062                                 _editor->selection->set (_marker);
4063                                 _selection_changed = true;
4064                         }
4065                         break;
4066
4067                 case Selection::Toggle:
4068                         /* we toggle on the button release, click only */
4069                         _editor->selection->toggle (_marker);
4070                         _selection_changed = true;
4071
4072                         break;
4073
4074                 case Selection::Extend:
4075                 case Selection::Add:
4076                         break;
4077                 }
4078
4079                 if (_selection_changed) {
4080                         _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4081                         _editor->commit_reversible_selection_op();
4082                 }
4083
4084                 return;
4085         }
4086
4087         _editor->_dragging_edit_point = false;
4088
4089         XMLNode &before = _editor->session()->locations()->get_state();
4090         bool in_command = false;
4091
4092         MarkerSelection::iterator i;
4093         CopiedLocationInfo::iterator x;
4094         bool is_start;
4095
4096         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4097              x != _copied_locations.end() && i != _editor->selection->markers.end();
4098              ++i, ++x) {
4099
4100                 Location * location = _editor->find_location_from_marker (*i, is_start);
4101
4102                 if (location) {
4103
4104                         if (location->locked()) {
4105                                 continue;
4106                         }
4107                         if (!in_command) {
4108                                 _editor->begin_reversible_command ( _("move marker") );
4109                                 in_command = true;
4110                         }
4111                         if (location->is_mark()) {
4112                                 location->set_start (((*x).location)->start());
4113                         } else {
4114                                 location->set (((*x).location)->start(), ((*x).location)->end());
4115                         }
4116                 }
4117         }
4118
4119         if (in_command) {
4120                 XMLNode &after = _editor->session()->locations()->get_state();
4121                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4122                 _editor->commit_reversible_command ();
4123         }
4124 }
4125
4126 void
4127 MarkerDrag::aborted (bool movement_occured)
4128 {
4129         if (!movement_occured) {
4130                 return;
4131         }
4132
4133         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4134
4135                 /* move all markers to their original location */
4136
4137
4138                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4139
4140                         bool is_start;
4141                         Location * location = _editor->find_location_from_marker (*m, is_start);
4142
4143                         if (location) {
4144                                 (*m)->set_position (is_start ? location->start() : location->end());
4145                         }
4146                 }
4147         }
4148 }
4149
4150 void
4151 MarkerDrag::update_item (Location*)
4152 {
4153         /* noop */
4154 }
4155
4156 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4157         : Drag (e, i)
4158         , _fixed_grab_x (0.0)
4159         , _fixed_grab_y (0.0)
4160         , _cumulative_x_drag (0.0)
4161         , _cumulative_y_drag (0.0)
4162         , _pushing (false)
4163         , _final_index (0)
4164 {
4165         if (_zero_gain_fraction < 0.0) {
4166                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4167         }
4168
4169         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4170
4171         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4172         assert (_point);
4173 }
4174
4175
4176 void
4177 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4178 {
4179         Drag::start_grab (event, _editor->cursors()->fader);
4180
4181         // start the grab at the center of the control point so
4182         // the point doesn't 'jump' to the mouse after the first drag
4183         _fixed_grab_x = _point->get_x();
4184         _fixed_grab_y = _point->get_y();
4185
4186         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4187         setup_snap_delta (pos);
4188
4189         float const fraction = 1 - (_point->get_y() / _point->line().height());
4190         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4191
4192         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4193
4194         if (!_point->can_slide ()) {
4195                 _x_constrained = true;
4196         }
4197 }
4198
4199 void
4200 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4201 {
4202         double dx = _drags->current_pointer_x() - last_pointer_x();
4203         double dy = current_pointer_y() - last_pointer_y();
4204         bool need_snap = true;
4205
4206         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4207                 dx *= 0.1;
4208                 dy *= 0.1;
4209                 need_snap = false;
4210         }
4211
4212         /* coordinate in pixels relative to the start of the region (for region-based automation)
4213            or track (for track-based automation) */
4214         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4215         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4216
4217         // calculate zero crossing point. back off by .01 to stay on the
4218         // positive side of zero
4219         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4220
4221         if (_x_constrained) {
4222                 cx = _fixed_grab_x;
4223         }
4224         if (_y_constrained) {
4225                 cy = _fixed_grab_y;
4226         }
4227
4228         _cumulative_x_drag = cx - _fixed_grab_x;
4229         _cumulative_y_drag = cy - _fixed_grab_y;
4230
4231         cx = max (0.0, cx);
4232         cy = max (0.0, cy);
4233         cy = min ((double) _point->line().height(), cy);
4234
4235         // make sure we hit zero when passing through
4236         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4237                 cy = zero_gain_y;
4238         }
4239
4240         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4241
4242         if (!_x_constrained && need_snap) {
4243                 _editor->snap_to_with_modifier (cx_frames, event);
4244         }
4245
4246         cx_frames -= snap_delta (event->button.state);
4247         cx_frames = min (cx_frames, _point->line().maximum_time());
4248
4249         float const fraction = 1.0 - (cy / _point->line().height());
4250
4251         if (first_motion) {
4252                 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4253                 _editor->begin_reversible_command (_("automation event move"));
4254                 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4255         }
4256         pair<double, float> result;
4257         result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4258
4259         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4260 }
4261
4262 void
4263 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4264 {
4265         if (!movement_occurred) {
4266
4267                 /* just a click */
4268                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4269                         _editor->reset_point_selection ();
4270                 }
4271
4272         } else {
4273                 _point->line().end_drag (_pushing, _final_index);
4274                 _editor->commit_reversible_command ();
4275         }
4276 }
4277
4278 void
4279 ControlPointDrag::aborted (bool)
4280 {
4281         _point->line().reset ();
4282 }
4283
4284 bool
4285 ControlPointDrag::active (Editing::MouseMode m)
4286 {
4287         if (m == Editing::MouseDraw) {
4288                 /* always active in mouse draw */
4289                 return true;
4290         }
4291
4292         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4293         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4294 }
4295
4296 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4297         : Drag (e, i)
4298         , _line (0)
4299         , _fixed_grab_x (0.0)
4300         , _fixed_grab_y (0.0)
4301         , _cumulative_y_drag (0)
4302         , _before (0)
4303         , _after (0)
4304 {
4305         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4306 }
4307
4308 void
4309 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4310 {
4311         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4312         assert (_line);
4313
4314         _item = &_line->grab_item ();
4315
4316         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4317            origin, and ditto for y.
4318         */
4319
4320         double cx = event->button.x;
4321         double cy = event->button.y;
4322
4323         _line->parent_group().canvas_to_item (cx, cy);
4324
4325         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
4326
4327         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4328                 /* no adjacent points */
4329                 return;
4330         }
4331
4332         Drag::start_grab (event, _editor->cursors()->fader);
4333
4334         /* store grab start in parent frame */
4335
4336         _fixed_grab_x = cx;
4337         _fixed_grab_y = cy;
4338
4339         double fraction = 1.0 - (cy / _line->height());
4340
4341         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4342 }
4343
4344 void
4345 LineDrag::motion (GdkEvent* event, bool first_move)
4346 {
4347         double dy = current_pointer_y() - last_pointer_y();
4348
4349         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4350                 dy *= 0.1;
4351         }
4352
4353         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4354
4355         _cumulative_y_drag = cy - _fixed_grab_y;
4356
4357         cy = max (0.0, cy);
4358         cy = min ((double) _line->height(), cy);
4359
4360         double const fraction = 1.0 - (cy / _line->height());
4361         uint32_t ignored;
4362
4363         if (first_move) {
4364                 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4365
4366                 _editor->begin_reversible_command (_("automation range move"));
4367                 _line->start_drag_line (_before, _after, initial_fraction);
4368         }
4369
4370         /* we are ignoring x position for this drag, so we can just pass in anything */
4371         pair<double, float> result;
4372
4373         result = _line->drag_motion (0, fraction, true, false, ignored);
4374         show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4375 }
4376
4377 void
4378 LineDrag::finished (GdkEvent* event, bool movement_occured)
4379 {
4380         if (movement_occured) {
4381                 motion (event, false);
4382                 _line->end_drag (false, 0);
4383                 _editor->commit_reversible_command ();
4384         } else {
4385                 /* add a new control point on the line */
4386
4387                 AutomationTimeAxisView* atv;
4388
4389                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4390                         framepos_t where = _editor->canvas_event_sample (event, 0, 0);
4391
4392                         atv->add_automation_event (event, where, event->button.y, false);
4393                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4394                         AudioRegionView* arv;
4395
4396                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4397                                 arv->add_gain_point_event (arv->get_canvas_group (), event, false);
4398                         }
4399                 }
4400         }
4401 }
4402
4403 void
4404 LineDrag::aborted (bool)
4405 {
4406         _line->reset ();
4407 }
4408
4409 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4410         : Drag (e, i),
4411           _line (0),
4412           _arv (0),
4413           _region_view_grab_x (0.0),
4414           _cumulative_x_drag (0),
4415           _before (0.0),
4416           _max_x (0)
4417 {
4418         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4419 }
4420
4421 void
4422 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4423 {
4424         Drag::start_grab (event);
4425
4426         _line = reinterpret_cast<Line*> (_item);
4427         assert (_line);
4428
4429         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4430
4431         double cx = event->button.x;
4432         double cy = event->button.y;
4433
4434         _item->parent()->canvas_to_item (cx, cy);
4435
4436         /* store grab start in parent frame */
4437         _region_view_grab_x = cx;
4438
4439         _before = *(float*) _item->get_data ("position");
4440
4441         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4442
4443         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4444 }
4445
4446 void
4447 FeatureLineDrag::motion (GdkEvent*, bool)
4448 {
4449         double dx = _drags->current_pointer_x() - last_pointer_x();
4450
4451         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4452
4453         _cumulative_x_drag += dx;
4454
4455         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4456
4457         if (cx > _max_x){
4458                 cx = _max_x;
4459         }
4460         else if(cx < 0){
4461                 cx = 0;
4462         }
4463
4464         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4465         assert (bbox);
4466         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4467
4468         float *pos = new float;
4469         *pos = cx;
4470
4471         _line->set_data ("position", pos);
4472
4473         _before = cx;
4474 }
4475
4476 void
4477 FeatureLineDrag::finished (GdkEvent*, bool)
4478 {
4479         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4480         _arv->update_transient(_before, _before);
4481 }
4482
4483 void
4484 FeatureLineDrag::aborted (bool)
4485 {
4486         //_line->reset ();
4487 }
4488
4489 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4490         : Drag (e, i)
4491         , _vertical_only (false)
4492 {
4493         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4494 }
4495
4496 void
4497 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4498 {
4499         Drag::start_grab (event);
4500         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4501 }
4502
4503 void
4504 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4505 {
4506         framepos_t start;
4507         framepos_t end;
4508         double y1;
4509         double y2;
4510
4511         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4512
4513         framepos_t grab = grab_frame ();
4514         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4515                 _editor->snap_to_with_modifier (grab, event);
4516         } else {
4517                 grab = raw_grab_frame ();
4518         }
4519
4520         /* base start and end on initial click position */
4521
4522         if (pf < grab) {
4523                 start = pf;
4524                 end = grab;
4525         } else {
4526                 end = pf;
4527                 start = grab;
4528         }
4529
4530         if (current_pointer_y() < grab_y()) {
4531                 y1 = current_pointer_y();
4532                 y2 = grab_y();
4533         } else {
4534                 y2 = current_pointer_y();
4535                 y1 = grab_y();
4536         }
4537
4538         if (start != end || y1 != y2) {
4539
4540                 double x1 = _editor->sample_to_pixel (start);
4541                 double x2 = _editor->sample_to_pixel (end);
4542                 const double min_dimension = 2.0;
4543
4544                 if (_vertical_only) {
4545                         /* fixed 10 pixel width */
4546                         x2 = x1 + 10;
4547                 } else {
4548                         if (x2 < x1) {
4549                                 x2 = min (x1 - min_dimension, x2);
4550                         } else {
4551                                 x2 = max (x1 + min_dimension, x2);
4552                         }
4553                 }
4554
4555                 if (y2 < y1) {
4556                         y2 = min (y1 - min_dimension, y2);
4557                 } else {
4558                         y2 = max (y1 + min_dimension, y2);
4559                 }
4560
4561                 /* translate rect into item space and set */
4562
4563                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4564
4565                 /* this drag is a _trackview_only == true drag, so the y1 and
4566                  * y2 (computed using current_pointer_y() and grab_y()) will be
4567                  * relative to the top of the trackview group). The
4568                  * rubberband rect has the same parent/scroll offset as the
4569                  * the trackview group, so we can use the "r" rect directly
4570                  * to set the shape of the rubberband.
4571                  */
4572
4573                 _editor->rubberband_rect->set (r);
4574                 _editor->rubberband_rect->show();
4575                 _editor->rubberband_rect->raise_to_top();
4576
4577                 show_verbose_cursor_time (pf);
4578
4579                 do_select_things (event, true);
4580         }
4581 }
4582
4583 void
4584 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4585 {
4586         framepos_t x1;
4587         framepos_t x2;
4588         framepos_t grab = grab_frame ();
4589         framepos_t lpf = last_pointer_frame ();
4590
4591         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4592                 grab = raw_grab_frame ();
4593                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4594         }
4595
4596         if (grab < lpf) {
4597                 x1 = grab;
4598                 x2 = lpf;
4599         } else {
4600                 x2 = grab;
4601                 x1 = lpf;
4602         }
4603
4604         double y1;
4605         double y2;
4606
4607         if (current_pointer_y() < grab_y()) {
4608                 y1 = current_pointer_y();
4609                 y2 = grab_y();
4610         } else {
4611                 y2 = current_pointer_y();
4612                 y1 = grab_y();
4613         }
4614
4615         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4616 }
4617
4618 void
4619 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4620 {
4621         if (movement_occurred) {
4622
4623                 motion (event, false);
4624                 do_select_things (event, false);
4625
4626         } else {
4627
4628                 /* just a click */
4629
4630                 bool do_deselect = true;
4631                 MidiTimeAxisView* mtv;
4632
4633                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4634                         /* MIDI track */
4635                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4636                                 /* nothing selected */
4637                                 add_midi_region (mtv);
4638                                 do_deselect = false;
4639                         }
4640                 }
4641
4642                 /* do not deselect if Primary or Tertiary (toggle-select or
4643                  * extend-select are pressed.
4644                  */
4645
4646                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4647                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4648                     do_deselect) {
4649                         deselect_things ();
4650                 }
4651
4652         }
4653
4654         _editor->rubberband_rect->hide();
4655 }
4656
4657 void
4658 RubberbandSelectDrag::aborted (bool)
4659 {
4660         _editor->rubberband_rect->hide ();
4661 }
4662
4663 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4664         : RegionDrag (e, i, p, v)
4665 {
4666         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4667 }
4668
4669 void
4670 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4671 {
4672         Drag::start_grab (event, cursor);
4673
4674         _editor->get_selection().add (_primary);
4675
4676         framepos_t where = _primary->region()->position();
4677         setup_snap_delta (where);
4678
4679         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4680 }
4681
4682 void
4683 TimeFXDrag::motion (GdkEvent* event, bool)
4684 {
4685         RegionView* rv = _primary;
4686         StreamView* cv = rv->get_time_axis_view().view ();
4687
4688         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4689         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4690         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4691         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4692         _editor->snap_to_with_modifier (pf, event);
4693         pf -= snap_delta (event->button.state);
4694
4695         if (pf > rv->region()->position()) {
4696                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4697         }
4698
4699         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4700 }
4701
4702 void
4703 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4704 {
4705         _primary->get_time_axis_view().hide_timestretch ();
4706
4707         if (!movement_occurred) {
4708                 return;
4709         }
4710
4711         if (last_pointer_frame() < _primary->region()->position()) {
4712                 /* backwards drag of the left edge - not usable */
4713                 return;
4714         }
4715
4716         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4717
4718         float percentage = (double) newlen / (double) _primary->region()->length();
4719
4720 #ifndef USE_RUBBERBAND
4721         // Soundtouch uses percentage / 100 instead of normal (/ 1)
4722         if (_primary->region()->data_type() == DataType::AUDIO) {
4723                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4724         }
4725 #endif
4726
4727         if (!_editor->get_selection().regions.empty()) {
4728                 /* primary will already be included in the selection, and edit
4729                    group shared editing will propagate selection across
4730                    equivalent regions, so just use the current region
4731                    selection.
4732                 */
4733
4734                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4735                         error << _("An error occurred while executing time stretch operation") << endmsg;
4736                 }
4737         }
4738 }
4739
4740 void
4741 TimeFXDrag::aborted (bool)
4742 {
4743         _primary->get_time_axis_view().hide_timestretch ();
4744 }
4745
4746 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4747         : Drag (e, i)
4748 {
4749         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4750 }
4751
4752 void
4753 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4754 {
4755         Drag::start_grab (event);
4756 }
4757
4758 void
4759 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4760 {
4761         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4762 }
4763
4764 void
4765 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4766 {
4767         if (movement_occurred && _editor->session()) {
4768                 /* make sure we stop */
4769                 _editor->session()->request_transport_speed (0.0);
4770         }
4771 }
4772
4773 void
4774 ScrubDrag::aborted (bool)
4775 {
4776         /* XXX: TODO */
4777 }
4778
4779 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4780         : Drag (e, i)
4781         , _operation (o)
4782         , _add (false)
4783         , _time_selection_at_start (!_editor->get_selection().time.empty())
4784 {
4785         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4786
4787         if (_time_selection_at_start) {
4788                 start_at_start = _editor->get_selection().time.start();
4789                 end_at_start = _editor->get_selection().time.end_frame();
4790         }
4791 }
4792
4793 void
4794 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4795 {
4796         if (_editor->session() == 0) {
4797                 return;
4798         }
4799
4800         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4801
4802         switch (_operation) {
4803         case CreateSelection:
4804                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4805                         _add = true;
4806                 } else {
4807                         _add = false;
4808                 }
4809                 cursor = _editor->cursors()->selector;
4810                 Drag::start_grab (event, cursor);
4811                 break;
4812
4813         case SelectionStartTrim:
4814                 if (_editor->clicked_axisview) {
4815                         _editor->clicked_axisview->order_selection_trims (_item, true);
4816                 }
4817                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4818                 break;
4819
4820         case SelectionEndTrim:
4821                 if (_editor->clicked_axisview) {
4822                         _editor->clicked_axisview->order_selection_trims (_item, false);
4823                 }
4824                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4825                 break;
4826
4827         case SelectionMove:
4828                 Drag::start_grab (event, cursor);
4829                 break;
4830
4831         case SelectionExtend:
4832                 Drag::start_grab (event, cursor);
4833                 break;
4834         }
4835
4836         if (_operation == SelectionMove) {
4837                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4838         } else {
4839                 show_verbose_cursor_time (adjusted_current_frame (event));
4840         }
4841 }
4842
4843 void
4844 SelectionDrag::setup_pointer_frame_offset ()
4845 {
4846         switch (_operation) {
4847         case CreateSelection:
4848                 _pointer_frame_offset = 0;
4849                 break;
4850
4851         case SelectionStartTrim:
4852         case SelectionMove:
4853                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4854                 break;
4855
4856         case SelectionEndTrim:
4857                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4858                 break;
4859
4860         case SelectionExtend:
4861                 break;
4862         }
4863 }
4864
4865 void
4866 SelectionDrag::motion (GdkEvent* event, bool first_move)
4867 {
4868         framepos_t start = 0;
4869         framepos_t end = 0;
4870         framecnt_t length = 0;
4871         framecnt_t distance = 0;
4872
4873         framepos_t const pending_position = adjusted_current_frame (event);
4874
4875         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4876                 return;
4877         }
4878
4879         switch (_operation) {
4880         case CreateSelection:
4881         {
4882                 framepos_t grab = grab_frame ();
4883
4884                 if (first_move) {
4885                         grab = adjusted_current_frame (event, false);
4886                         if (grab < pending_position) {
4887                                 _editor->snap_to (grab, RoundDownMaybe);
4888                         }  else {
4889                                 _editor->snap_to (grab, RoundUpMaybe);
4890                         }
4891                 }
4892
4893                 if (pending_position < grab) {
4894                         start = pending_position;
4895                         end = grab;
4896                 } else {
4897                         end = pending_position;
4898                         start = grab;
4899                 }
4900
4901                 /* first drag: Either add to the selection
4902                    or create a new selection
4903                 */
4904
4905                 if (first_move) {
4906
4907                         if (_add) {
4908
4909                                 /* adding to the selection */
4910                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4911                                 _editor->clicked_selection = _editor->selection->add (start, end);
4912                                 _add = false;
4913
4914                         } else {
4915
4916                                 /* new selection */
4917
4918                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4919                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4920                                 }
4921
4922                                 _editor->clicked_selection = _editor->selection->set (start, end);
4923                         }
4924                 }
4925
4926                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
4927                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
4928                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4929                 if (atest) {
4930                         _editor->selection->add (atest);
4931                         break;
4932                 }
4933
4934                 /* select all tracks within the rectangle that we've marked out so far */
4935                 TrackViewList new_selection;
4936                 TrackViewList& all_tracks (_editor->track_views);
4937
4938                 ArdourCanvas::Coord const top = grab_y();
4939                 ArdourCanvas::Coord const bottom = current_pointer_y();
4940
4941                 if (top >= 0 && bottom >= 0) {
4942
4943                         //first, find the tracks that are covered in the y range selection
4944                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4945                                 if ((*i)->covered_by_y_range (top, bottom)) {
4946                                         new_selection.push_back (*i);
4947                                 }
4948                         }
4949
4950                         //now find any tracks that are GROUPED with the tracks we selected
4951                         TrackViewList grouped_add = new_selection;
4952                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4953                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4954                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4955                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4956                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4957                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4958                                                         grouped_add.push_back (*j);
4959                                         }
4960                                 }
4961                         }
4962
4963                         //now compare our list with the current selection, and add or remove as necessary
4964                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4965                         TrackViewList tracks_to_add;
4966                         TrackViewList tracks_to_remove;
4967                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4968                                 if ( !_editor->selection->tracks.contains ( *i ) )
4969                                         tracks_to_add.push_back ( *i );
4970                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4971                                 if ( !grouped_add.contains ( *i ) )
4972                                         tracks_to_remove.push_back ( *i );
4973                         _editor->selection->add(tracks_to_add);
4974                         _editor->selection->remove(tracks_to_remove);
4975
4976                 }
4977         }
4978         break;
4979
4980         case SelectionStartTrim:
4981
4982                 end = _editor->selection->time[_editor->clicked_selection].end;
4983
4984                 if (pending_position > end) {
4985                         start = end;
4986                 } else {
4987                         start = pending_position;
4988                 }
4989                 break;
4990
4991         case SelectionEndTrim:
4992
4993                 start = _editor->selection->time[_editor->clicked_selection].start;
4994
4995                 if (pending_position < start) {
4996                         end = start;
4997                 } else {
4998                         end = pending_position;
4999                 }
5000
5001                 break;
5002
5003         case SelectionMove:
5004
5005                 start = _editor->selection->time[_editor->clicked_selection].start;
5006                 end = _editor->selection->time[_editor->clicked_selection].end;
5007
5008                 length = end - start;
5009                 distance = pending_position - start;
5010                 start = pending_position;
5011                 _editor->snap_to (start);
5012
5013                 end = start + length;
5014
5015                 break;
5016
5017         case SelectionExtend:
5018                 break;
5019         }
5020
5021         if (start != end) {
5022                 switch (_operation) {
5023                 case SelectionMove:
5024                         if (_time_selection_at_start) {
5025                                 _editor->selection->move_time (distance);
5026                         }
5027                         break;
5028                 default:
5029                         _editor->selection->replace (_editor->clicked_selection, start, end);
5030                 }
5031         }
5032
5033         if (_operation == SelectionMove) {
5034                 show_verbose_cursor_time(start);
5035         } else {
5036                 show_verbose_cursor_time(pending_position);
5037         }
5038 }
5039
5040 void
5041 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5042 {
5043         Session* s = _editor->session();
5044
5045         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5046         if (movement_occurred) {
5047                 motion (event, false);
5048                 /* XXX this is not object-oriented programming at all. ick */
5049                 if (_editor->selection->time.consolidate()) {
5050                         _editor->selection->TimeChanged ();
5051                 }
5052
5053                 /* XXX what if its a music time selection? */
5054                 if (s) {
5055                         if (s->get_play_range() && s->transport_rolling()) {
5056                                 s->request_play_range (&_editor->selection->time, true);
5057                         } else if (!s->config.get_external_sync()) {
5058                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5059                                         if (_operation == SelectionEndTrim)
5060                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5061                                         else
5062                                                 s->request_locate (_editor->get_selection().time.start());
5063                                 }
5064                         }
5065
5066                         if (_editor->get_selection().time.length() != 0) {
5067                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5068                         } else {
5069                                 s->clear_range_selection ();
5070                         }
5071                 }
5072
5073         } else {
5074                 /* just a click, no pointer movement.
5075                  */
5076
5077                 if (_operation == SelectionExtend) {
5078                         if (_time_selection_at_start) {
5079                                 framepos_t pos = adjusted_current_frame (event, false);
5080                                 framepos_t start = min (pos, start_at_start);
5081                                 framepos_t end = max (pos, end_at_start);
5082                                 _editor->selection->set (start, end);
5083                         }
5084                 } else {
5085                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5086                                 if (_editor->clicked_selection) {
5087                                         _editor->selection->remove (_editor->clicked_selection);
5088                                 }
5089                         } else {
5090                                 if (!_editor->clicked_selection) {
5091                                         _editor->selection->clear_time();
5092                                 }
5093                         }
5094                 }
5095
5096                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5097                         _editor->selection->set (_editor->clicked_axisview);
5098                 }
5099
5100                 if (s && s->get_play_range () && s->transport_rolling()) {
5101                         s->request_stop (false, false);
5102                 }
5103
5104         }
5105
5106         _editor->stop_canvas_autoscroll ();
5107         _editor->clicked_selection = 0;
5108         _editor->commit_reversible_selection_op ();
5109 }
5110
5111 void
5112 SelectionDrag::aborted (bool)
5113 {
5114         /* XXX: TODO */
5115 }
5116
5117 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5118         : Drag (e, i, false),
5119           _operation (o),
5120           _copy (false)
5121 {
5122         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5123
5124         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5125                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5126                                                                       physical_screen_height (_editor->get_window())));
5127         _drag_rect->hide ();
5128
5129         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5130         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5131 }
5132
5133 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5134 {
5135         /* normal canvas items will be cleaned up when their parent group is deleted. But
5136            this item is created as the child of a long-lived parent group, and so we
5137            need to explicitly delete it.
5138         */
5139         delete _drag_rect;
5140 }
5141
5142 void
5143 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5144 {
5145         if (_editor->session() == 0) {
5146                 return;
5147         }
5148
5149         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5150
5151         if (!_editor->temp_location) {
5152                 _editor->temp_location = new Location (*_editor->session());
5153         }
5154
5155         switch (_operation) {
5156         case CreateSkipMarker:
5157         case CreateRangeMarker:
5158         case CreateTransportMarker:
5159         case CreateCDMarker:
5160
5161                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5162                         _copy = true;
5163                 } else {
5164                         _copy = false;
5165                 }
5166                 cursor = _editor->cursors()->selector;
5167                 break;
5168         }
5169
5170         Drag::start_grab (event, cursor);
5171
5172         show_verbose_cursor_time (adjusted_current_frame (event));
5173 }
5174
5175 void
5176 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5177 {
5178         framepos_t start = 0;
5179         framepos_t end = 0;
5180         ArdourCanvas::Rectangle *crect;
5181
5182         switch (_operation) {
5183         case CreateSkipMarker:
5184                 crect = _editor->range_bar_drag_rect;
5185                 break;
5186         case CreateRangeMarker:
5187                 crect = _editor->range_bar_drag_rect;
5188                 break;
5189         case CreateTransportMarker:
5190                 crect = _editor->transport_bar_drag_rect;
5191                 break;
5192         case CreateCDMarker:
5193                 crect = _editor->cd_marker_bar_drag_rect;
5194                 break;
5195         default:
5196                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5197                 return;
5198                 break;
5199         }
5200
5201         framepos_t const pf = adjusted_current_frame (event);
5202
5203         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5204                 framepos_t grab = grab_frame ();
5205                 _editor->snap_to (grab);
5206
5207                 if (pf < grab_frame()) {
5208                         start = pf;
5209                         end = grab;
5210                 } else {
5211                         end = pf;
5212                         start = grab;
5213                 }
5214
5215                 /* first drag: Either add to the selection
5216                    or create a new selection.
5217                 */
5218
5219                 if (first_move) {
5220
5221                         _editor->temp_location->set (start, end);
5222
5223                         crect->show ();
5224
5225                         update_item (_editor->temp_location);
5226                         _drag_rect->show();
5227                         //_drag_rect->raise_to_top();
5228
5229                 }
5230         }
5231
5232         if (start != end) {
5233                 _editor->temp_location->set (start, end);
5234
5235                 double x1 = _editor->sample_to_pixel (start);
5236                 double x2 = _editor->sample_to_pixel (end);
5237                 crect->set_x0 (x1);
5238                 crect->set_x1 (x2);
5239
5240                 update_item (_editor->temp_location);
5241         }
5242
5243         show_verbose_cursor_time (pf);
5244
5245 }
5246
5247 void
5248 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5249 {
5250         Location * newloc = 0;
5251         string rangename;
5252         int flags;
5253
5254         if (movement_occurred) {
5255                 motion (event, false);
5256                 _drag_rect->hide();
5257
5258                 switch (_operation) {
5259                 case CreateSkipMarker:
5260                 case CreateRangeMarker:
5261                 case CreateCDMarker:
5262                     {
5263                         XMLNode &before = _editor->session()->locations()->get_state();
5264                         if (_operation == CreateSkipMarker) {
5265                                 _editor->begin_reversible_command (_("new skip marker"));
5266                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5267                                 flags = Location::IsRangeMarker | Location::IsSkip;
5268                                 _editor->range_bar_drag_rect->hide();
5269                         } else if (_operation == CreateCDMarker) {
5270                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5271                                 _editor->begin_reversible_command (_("new CD marker"));
5272                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5273                                 _editor->cd_marker_bar_drag_rect->hide();
5274                         } else {
5275                                 _editor->begin_reversible_command (_("new skip marker"));
5276                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5277                                 flags = Location::IsRangeMarker;
5278                                 _editor->range_bar_drag_rect->hide();
5279                         }
5280                         newloc = new Location (
5281                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5282                                 );
5283
5284                         _editor->session()->locations()->add (newloc, true);
5285                         XMLNode &after = _editor->session()->locations()->get_state();
5286                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5287                         _editor->commit_reversible_command ();
5288                         break;
5289                     }
5290
5291                 case CreateTransportMarker:
5292                         // popup menu to pick loop or punch
5293                         _editor->new_transport_marker_context_menu (&event->button, _item);
5294                         break;
5295                 }
5296
5297         } else {
5298
5299                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5300
5301                 if (_operation == CreateTransportMarker) {
5302
5303                         /* didn't drag, so just locate */
5304
5305                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5306
5307                 } else if (_operation == CreateCDMarker) {
5308
5309                         /* didn't drag, but mark is already created so do
5310                          * nothing */
5311
5312                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5313
5314                         framepos_t start;
5315                         framepos_t end;
5316
5317                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5318
5319                         if (end == max_framepos) {
5320                                 end = _editor->session()->current_end_frame ();
5321                         }
5322
5323                         if (start == max_framepos) {
5324                                 start = _editor->session()->current_start_frame ();
5325                         }
5326
5327                         switch (_editor->mouse_mode) {
5328                         case MouseObject:
5329                                 /* find the two markers on either side and then make the selection from it */
5330                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5331                                 break;
5332
5333                         case MouseRange:
5334                                 /* find the two markers on either side of the click and make the range out of it */
5335                                 _editor->selection->set (start, end);
5336                                 break;
5337
5338                         default:
5339                                 break;
5340                         }
5341                 }
5342         }
5343
5344         _editor->stop_canvas_autoscroll ();
5345 }
5346
5347 void
5348 RangeMarkerBarDrag::aborted (bool movement_occured)
5349 {
5350         if (movement_occured) {
5351                 _drag_rect->hide ();
5352         }
5353 }
5354
5355 void
5356 RangeMarkerBarDrag::update_item (Location* location)
5357 {
5358         double const x1 = _editor->sample_to_pixel (location->start());
5359         double const x2 = _editor->sample_to_pixel (location->end());
5360
5361         _drag_rect->set_x0 (x1);
5362         _drag_rect->set_x1 (x2);
5363 }
5364
5365 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5366         : Drag (e, i)
5367         , _cumulative_dx (0)
5368         , _cumulative_dy (0)
5369         , _was_selected (false)
5370 {
5371         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5372
5373         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5374         assert (_primary);
5375         _region = &_primary->region_view ();
5376         _note_height = _region->midi_stream_view()->note_height ();
5377 }
5378
5379 void
5380 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5381 {
5382         Drag::start_grab (event);
5383         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5384
5385         if (!(_was_selected = _primary->selected())) {
5386
5387                 /* tertiary-click means extend selection - we'll do that on button release,
5388                    so don't add it here, because otherwise we make it hard to figure
5389                    out the "extend-to" range.
5390                 */
5391
5392                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5393
5394                 if (!extend) {
5395                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5396
5397                         if (add) {
5398                                 _region->note_selected (_primary, true);
5399                         } else {
5400                                 _editor->get_selection().clear_points();
5401                                 _region->unique_select (_primary);
5402                         }
5403                 }
5404         }
5405 }
5406
5407 /** @return Current total drag x change in frames */
5408 frameoffset_t
5409 NoteDrag::total_dx (const guint state) const
5410 {
5411         /* dx in frames */
5412         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5413
5414         /* primary note time */
5415         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
5416
5417         /* new time of the primary note in session frames */
5418         frameoffset_t st = n + dx + snap_delta (state);
5419
5420         framepos_t const rp = _region->region()->position ();
5421
5422         /* prevent the note being dragged earlier than the region's position */
5423         st = max (st, rp);
5424
5425         /* possibly snap and return corresponding delta */
5426
5427         bool snap = true;
5428
5429         if (ArdourKeyboard::indicates_snap (state)) {
5430                 if (_editor->snap_mode () != SnapOff) {
5431                         snap = false;
5432                 }
5433         } else {
5434                 if (_editor->snap_mode () == SnapOff) {
5435                         snap = false;
5436                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5437                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5438                                 snap = true;
5439                         }
5440                 }
5441         }
5442
5443         frameoffset_t ret;
5444         if (snap) {
5445                 ret =  _region->snap_frame_to_frame (st - rp) + rp - n - snap_delta (state);
5446         } else {
5447                 ret = st - n - snap_delta (state);
5448         }
5449         return ret;
5450 }
5451
5452 /** @return Current total drag y change in note number */
5453 int8_t
5454 NoteDrag::total_dy () const
5455 {
5456         MidiStreamView* msv = _region->midi_stream_view ();
5457         double const y = _region->midi_view()->y_position ();
5458         /* new current note */
5459         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5460         /* clamp */
5461         n = max (msv->lowest_note(), n);
5462         n = min (msv->highest_note(), n);
5463         /* and work out delta */
5464         return n - msv->y_to_note (grab_y() - y);
5465 }
5466
5467 void
5468 NoteDrag::motion (GdkEvent * event, bool)
5469 {
5470         /* Total change in x and y since the start of the drag */
5471         frameoffset_t const dx = total_dx (event->button.state);
5472         int8_t const dy = total_dy ();
5473
5474         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5475         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
5476         double const tdy = -dy * _note_height - _cumulative_dy;
5477
5478         if (tdx || tdy) {
5479                 _cumulative_dx += tdx;
5480                 _cumulative_dy += tdy;
5481
5482                 int8_t note_delta = total_dy();
5483
5484                 _region->move_selection (tdx, tdy, note_delta);
5485
5486                 /* the new note value may be the same as the old one, but we
5487                  * don't know what that means because the selection may have
5488                  * involved more than one note and we might be doing something
5489                  * odd with them. so show the note value anyway, always.
5490                  */
5491
5492                 char buf[12];
5493                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5494
5495                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
5496                           (int) floor ((double)new_note));
5497
5498                 show_verbose_cursor_text (buf);
5499         }
5500 }
5501
5502 void
5503 NoteDrag::finished (GdkEvent* ev, bool moved)
5504 {
5505         if (!moved) {
5506                 /* no motion - select note */
5507
5508                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5509                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5510
5511                         bool changed = false;
5512
5513                         if (_was_selected) {
5514                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5515                                 if (add) {
5516                                         _region->note_deselected (_primary);
5517                                         changed = true;
5518                                 } else {
5519                                         _editor->get_selection().clear_points();
5520                                         _region->unique_select (_primary);
5521                                         changed = true;
5522                                 }
5523                         } else {
5524                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5525                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5526
5527                                 if (!extend && !add && _region->selection_size() > 1) {
5528                                         _editor->get_selection().clear_points();
5529                                         _region->unique_select (_primary);
5530                                         changed = true;
5531                                 } else if (extend) {
5532                                         _region->note_selected (_primary, true, true);
5533                                         changed = true;
5534                                 } else {
5535                                         /* it was added during button press */
5536                                         changed = true;
5537
5538                                 }
5539                         }
5540
5541                         if (changed) {
5542                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5543                                 _editor->commit_reversible_selection_op();
5544                         }
5545                 }
5546         } else {
5547                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5548         }
5549 }
5550
5551 void
5552 NoteDrag::aborted (bool)
5553 {
5554         /* XXX: TODO */
5555 }
5556
5557 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5558 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5559         : Drag (editor, atv->base_item ())
5560         , _ranges (r)
5561         , _y_origin (atv->y_position())
5562         , _nothing_to_drag (false)
5563 {
5564         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5565         setup (atv->lines ());
5566 }
5567
5568 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5569 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5570         : Drag (editor, rv->get_canvas_group ())
5571         , _ranges (r)
5572         , _y_origin (rv->get_time_axis_view().y_position())
5573         , _nothing_to_drag (false)
5574         , _integral (false)
5575 {
5576         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5577
5578         list<boost::shared_ptr<AutomationLine> > lines;
5579
5580         AudioRegionView*      audio_view;
5581         AutomationRegionView* automation_view;
5582         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5583                 lines.push_back (audio_view->get_gain_line ());
5584         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5585                 lines.push_back (automation_view->line ());
5586                 _integral = true;
5587         } else {
5588                 error << _("Automation range drag created for invalid region type") << endmsg;
5589         }
5590
5591         setup (lines);
5592 }
5593
5594 /** @param lines AutomationLines to drag.
5595  *  @param offset Offset from the session start to the points in the AutomationLines.
5596  */
5597 void
5598 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5599 {
5600         /* find the lines that overlap the ranges being dragged */
5601         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5602         while (i != lines.end ()) {
5603                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5604                 ++j;
5605
5606                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5607
5608                 /* check this range against all the AudioRanges that we are using */
5609                 list<AudioRange>::const_iterator k = _ranges.begin ();
5610                 while (k != _ranges.end()) {
5611                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5612                                 break;
5613                         }
5614                         ++k;
5615                 }
5616
5617                 /* add it to our list if it overlaps at all */
5618                 if (k != _ranges.end()) {
5619                         Line n;
5620                         n.line = *i;
5621                         n.state = 0;
5622                         n.range = r;
5623                         _lines.push_back (n);
5624                 }
5625
5626                 i = j;
5627         }
5628
5629         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5630 }
5631
5632 double
5633 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5634 {
5635         return 1.0 - ((global_y - _y_origin) / line->height());
5636 }
5637
5638 double
5639 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5640 {
5641         const double v = list->eval(x);
5642         return _integral ? rint(v) : v;
5643 }
5644
5645 void
5646 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5647 {
5648         Drag::start_grab (event, cursor);
5649
5650         /* Get line states before we start changing things */
5651         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5652                 i->state = &i->line->get_state ();
5653                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5654         }
5655
5656         if (_ranges.empty()) {
5657
5658                 /* No selected time ranges: drag all points */
5659                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5660                         uint32_t const N = i->line->npoints ();
5661                         for (uint32_t j = 0; j < N; ++j) {
5662                                 i->points.push_back (i->line->nth (j));
5663                         }
5664                 }
5665
5666         }
5667
5668         if (_nothing_to_drag) {
5669                 return;
5670         }
5671 }
5672
5673 void
5674 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5675 {
5676         if (_nothing_to_drag && !first_move) {
5677                 return;
5678         }
5679
5680         if (first_move) {
5681                 _editor->begin_reversible_command (_("automation range move"));
5682
5683                 if (!_ranges.empty()) {
5684
5685                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5686
5687                                 framecnt_t const half = (i->start + i->end) / 2;
5688
5689                                 /* find the line that this audio range starts in */
5690                                 list<Line>::iterator j = _lines.begin();
5691                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5692                                         ++j;
5693                                 }
5694
5695                                 if (j != _lines.end()) {
5696                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5697
5698                                 /* j is the line that this audio range starts in; fade into it;
5699                                    64 samples length plucked out of thin air.
5700                                 */
5701
5702                                         framepos_t a = i->start + 64;
5703                                         if (a > half) {
5704                                                 a = half;
5705                                         }
5706
5707                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5708                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5709
5710                                         XMLNode &before = the_list->get_state();
5711                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5712                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5713
5714                                         if (add_p || add_q) {
5715                                                 _editor->session()->add_command (
5716                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5717                                         }
5718                                 }
5719
5720                                 /* same thing for the end */
5721
5722                                 j = _lines.begin();
5723                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5724                                         ++j;
5725                                 }
5726
5727                                 if (j != _lines.end()) {
5728                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5729
5730                                         /* j is the line that this audio range starts in; fade out of it;
5731                                            64 samples length plucked out of thin air.
5732                                         */
5733
5734                                         framepos_t b = i->end - 64;
5735                                         if (b < half) {
5736                                                 b = half;
5737                                         }
5738
5739                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5740                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5741
5742                                         XMLNode &before = the_list->get_state();
5743                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5744                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5745
5746                                         if (add_p || add_q) {
5747                                                 _editor->session()->add_command (
5748                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5749                                         }
5750                                 }
5751                         }
5752
5753                         _nothing_to_drag = true;
5754
5755                         /* Find all the points that should be dragged and put them in the relevant
5756                            points lists in the Line structs.
5757                         */
5758
5759                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5760
5761                                 uint32_t const N = i->line->npoints ();
5762                                 for (uint32_t j = 0; j < N; ++j) {
5763
5764                                         /* here's a control point on this line */
5765                                         ControlPoint* p = i->line->nth (j);
5766                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5767
5768                                         /* see if it's inside a range */
5769                                         list<AudioRange>::const_iterator k = _ranges.begin ();
5770                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5771                                                 ++k;
5772                                         }
5773
5774                                         if (k != _ranges.end()) {
5775                                                 /* dragging this point */
5776                                                 _nothing_to_drag = false;
5777                                                 i->points.push_back (p);
5778                                         }
5779                                 }
5780                         }
5781                 }
5782
5783                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5784                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5785                 }
5786         }
5787
5788         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5789                 float const f = y_fraction (l->line, current_pointer_y());
5790                 /* we are ignoring x position for this drag, so we can just pass in anything */
5791                 pair<double, float> result;
5792                 uint32_t ignored;
5793                 result = l->line->drag_motion (0, f, true, false, ignored);
5794                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5795         }
5796 }
5797
5798 void
5799 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5800 {
5801         if (_nothing_to_drag || !motion_occurred) {
5802                 return;
5803         }
5804
5805         motion (event, false);
5806         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5807                 i->line->end_drag (false, 0);
5808         }
5809
5810         _editor->commit_reversible_command ();
5811 }
5812
5813 void
5814 AutomationRangeDrag::aborted (bool)
5815 {
5816         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5817                 i->line->reset ();
5818         }
5819 }
5820
5821 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5822         : view (v)
5823         , initial_time_axis_view (itav)
5824 {
5825         /* note that time_axis_view may be null if the regionview was created
5826          * as part of a copy operation.
5827          */
5828         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5829         layer = v->region()->layer ();
5830         initial_y = v->get_canvas_group()->position().y;
5831         initial_playlist = v->region()->playlist ();
5832         initial_position = v->region()->position ();
5833         initial_end = v->region()->position () + v->region()->length ();
5834 }
5835
5836 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5837         : Drag (e, i->canvas_item ())
5838         , _region_view (r)
5839         , _patch_change (i)
5840         , _cumulative_dx (0)
5841 {
5842         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5843                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5844                                                    grab_frame()));
5845 }
5846
5847 void
5848 PatchChangeDrag::motion (GdkEvent* ev, bool)
5849 {
5850         framepos_t f = adjusted_current_frame (ev);
5851         boost::shared_ptr<Region> r = _region_view->region ();
5852         f = max (f, r->position ());
5853         f = min (f, r->last_frame ());
5854
5855         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5856         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5857         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5858         _cumulative_dx = dxu;
5859 }
5860
5861 void
5862 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5863 {
5864         if (!movement_occurred) {
5865                 return;
5866         }
5867
5868         boost::shared_ptr<Region> r (_region_view->region ());
5869         framepos_t f = adjusted_current_frame (ev);
5870         f = max (f, r->position ());
5871         f = min (f, r->last_frame ());
5872
5873         _region_view->move_patch_change (
5874                 *_patch_change,
5875                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5876                 );
5877 }
5878
5879 void
5880 PatchChangeDrag::aborted (bool)
5881 {
5882         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5883 }
5884
5885 void
5886 PatchChangeDrag::setup_pointer_frame_offset ()
5887 {
5888         boost::shared_ptr<Region> region = _region_view->region ();
5889         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5890 }
5891
5892 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5893         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5894         , _region_view (rv)
5895 {
5896
5897 }
5898
5899 void
5900 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5901 {
5902         _region_view->update_drag_selection (
5903                 x1, x2, y1, y2,
5904                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5905 }
5906
5907 void
5908 MidiRubberbandSelectDrag::deselect_things ()
5909 {
5910         /* XXX */
5911 }
5912
5913 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5914         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5915         , _region_view (rv)
5916 {
5917         _vertical_only = true;
5918 }
5919
5920 void
5921 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5922 {
5923         double const y = _region_view->midi_view()->y_position ();
5924
5925         y1 = max (0.0, y1 - y);
5926         y2 = max (0.0, y2 - y);
5927
5928         _region_view->update_vertical_drag_selection (
5929                 y1,
5930                 y2,
5931                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5932                 );
5933 }
5934
5935 void
5936 MidiVerticalSelectDrag::deselect_things ()
5937 {
5938         /* XXX */
5939 }
5940
5941 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5942         : RubberbandSelectDrag (e, i)
5943 {
5944
5945 }
5946
5947 void
5948 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5949 {
5950         if (drag_in_progress) {
5951                 /* We just want to select things at the end of the drag, not during it */
5952                 return;
5953         }
5954
5955         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5956
5957         _editor->begin_reversible_selection_op (X_("rubberband selection"));
5958
5959         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5960
5961         _editor->commit_reversible_selection_op ();
5962 }
5963
5964 void
5965 EditorRubberbandSelectDrag::deselect_things ()
5966 {
5967         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
5968
5969         _editor->selection->clear_tracks();
5970         _editor->selection->clear_regions();
5971         _editor->selection->clear_points ();
5972         _editor->selection->clear_lines ();
5973         _editor->selection->clear_midi_notes ();
5974
5975         _editor->commit_reversible_selection_op();
5976 }
5977
5978 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5979         : Drag (e, i)
5980         , _region_view (rv)
5981         , _drag_rect (0)
5982 {
5983         _note[0] = _note[1] = 0;
5984 }
5985
5986 NoteCreateDrag::~NoteCreateDrag ()
5987 {
5988         delete _drag_rect;
5989 }
5990
5991 framecnt_t
5992 NoteCreateDrag::grid_frames (framepos_t t) const
5993 {
5994         bool success;
5995         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5996         if (!success) {
5997                 grid_beats = Evoral::Beats(1);
5998         }
5999
6000         return _region_view->region_beats_to_region_frames (grid_beats);
6001 }
6002
6003 void
6004 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6005 {
6006         Drag::start_grab (event, cursor);
6007
6008         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6009
6010         framepos_t pf = _drags->current_pointer_frame ();
6011         framecnt_t const g = grid_frames (pf);
6012
6013         /* Hack so that we always snap to the note that we are over, instead of snapping
6014            to the next one if we're more than halfway through the one we're over.
6015         */
6016         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
6017                 pf -= g / 2;
6018         }
6019
6020         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
6021         _note[1] = _note[0];
6022
6023         MidiStreamView* sv = _region_view->midi_stream_view ();
6024         double const x = _editor->sample_to_pixel (_note[0]);
6025         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6026
6027         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6028         _drag_rect->set_outline_all ();
6029         _drag_rect->set_outline_color (0xffffff99);
6030         _drag_rect->set_fill_color (0xffffff66);
6031 }
6032
6033 void
6034 NoteCreateDrag::motion (GdkEvent* event, bool)
6035 {
6036         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6037         double const x0 = _editor->sample_to_pixel (_note[0]);
6038         double const x1 = _editor->sample_to_pixel (_note[1]);
6039         _drag_rect->set_x0 (std::min(x0, x1));
6040         _drag_rect->set_x1 (std::max(x0, x1));
6041 }
6042
6043 void
6044 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
6045 {
6046         if (!had_movement) {
6047                 return;
6048         }
6049
6050         framepos_t const start = min (_note[0], _note[1]);
6051         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6052
6053         framecnt_t const g = grid_frames (start);
6054         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6055
6056         if (_editor->snap_mode() == SnapNormal && length < g) {
6057                 length = g;
6058         }
6059
6060         Evoral::Beats length_beats = max (
6061                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
6062
6063         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
6064 }
6065
6066 double
6067 NoteCreateDrag::y_to_region (double y) const
6068 {
6069         double x = 0;
6070         _region_view->get_canvas_group()->canvas_to_item (x, y);
6071         return y;
6072 }
6073
6074 void
6075 NoteCreateDrag::aborted (bool)
6076 {
6077
6078 }
6079
6080 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6081         : Drag (e, i)
6082         , arv (rv)
6083         , start (start_yn)
6084 {
6085         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
6086 }
6087
6088 void
6089 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6090 {
6091         Drag::start_grab (event, cursor);
6092 }
6093
6094 void
6095 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6096 {
6097         double distance;
6098         double new_length;
6099         framecnt_t len;
6100
6101         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6102
6103         if (start) {
6104                 distance = _drags->current_pointer_x() - grab_x();
6105                 len = ar->fade_in()->back()->when;
6106         } else {
6107                 distance = grab_x() - _drags->current_pointer_x();
6108                 len = ar->fade_out()->back()->when;
6109         }
6110
6111         /* how long should it be ? */
6112
6113         new_length = len + _editor->pixel_to_sample (distance);
6114
6115         /* now check with the region that this is legal */
6116
6117         new_length = ar->verify_xfade_bounds (new_length, start);
6118
6119         if (start) {
6120                 arv->reset_fade_in_shape_width (ar, new_length);
6121         } else {
6122                 arv->reset_fade_out_shape_width (ar, new_length);
6123         }
6124 }
6125
6126 void
6127 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6128 {
6129         double distance;
6130         double new_length;
6131         framecnt_t len;
6132
6133         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6134
6135         if (start) {
6136                 distance = _drags->current_pointer_x() - grab_x();
6137                 len = ar->fade_in()->back()->when;
6138         } else {
6139                 distance = grab_x() - _drags->current_pointer_x();
6140                 len = ar->fade_out()->back()->when;
6141         }
6142
6143         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6144
6145         _editor->begin_reversible_command ("xfade trim");
6146         ar->playlist()->clear_owned_changes ();
6147
6148         if (start) {
6149                 ar->set_fade_in_length (new_length);
6150         } else {
6151                 ar->set_fade_out_length (new_length);
6152         }
6153
6154         /* Adjusting the xfade may affect other regions in the playlist, so we need
6155            to get undo Commands from the whole playlist rather than just the
6156            region.
6157         */
6158
6159         vector<Command*> cmds;
6160         ar->playlist()->rdiff (cmds);
6161         _editor->session()->add_commands (cmds);
6162         _editor->commit_reversible_command ();
6163
6164 }
6165
6166 void
6167 CrossfadeEdgeDrag::aborted (bool)
6168 {
6169         if (start) {
6170                 // arv->redraw_start_xfade ();
6171         } else {
6172                 // arv->redraw_end_xfade ();
6173         }
6174 }
6175
6176 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6177         : Drag (e, item, true)
6178         , line (new EditorCursor (*e))
6179 {
6180         line->set_position (pos);
6181         line->show ();
6182 }
6183
6184 RegionCutDrag::~RegionCutDrag ()
6185 {
6186         delete line;
6187 }
6188
6189 void
6190 RegionCutDrag::motion (GdkEvent*, bool)
6191 {
6192         framepos_t where = _drags->current_pointer_frame();
6193         _editor->snap_to (where);
6194
6195         line->set_position (where);
6196 }
6197
6198 void
6199 RegionCutDrag::finished (GdkEvent*, bool)
6200 {
6201         _editor->get_track_canvas()->canvas()->re_enter();
6202
6203         framepos_t pos = _drags->current_pointer_frame();
6204
6205         line->hide ();
6206
6207         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6208
6209         if (rs.empty()) {
6210                 return;
6211         }
6212
6213         _editor->split_regions_at (pos, rs);
6214 }
6215
6216 void
6217 RegionCutDrag::aborted (bool)
6218 {
6219 }