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