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