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