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