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