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