mp4chaps Lua script: don't clutter global environment
[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 framepos_t frame = map.frame_at_bbt (bbt);
3312                         _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3313                                                        , bbt, frame, _real_section->position_lock_style());
3314                         if (!_real_section) {
3315                                 aborted (true);
3316                                 return;
3317                         }
3318
3319                 }
3320                 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3321                 if (_real_section->position_lock_style() != AudioTime) {
3322                         _editor->set_snap_to (SnapToBar);
3323                         _editor->set_snap_mode (SnapNormal);
3324                 }
3325         }
3326
3327         framepos_t pf = adjusted_current_frame (event);
3328
3329         if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3330                 /* never snap to music for audio locked */
3331                 pf = adjusted_current_frame (event, false);
3332         }
3333
3334         _editor->session()->tempo_map().gui_set_meter_position (_real_section, pf);
3335
3336         /* fake marker meeds to stay under the mouse, unlike the real one. */
3337         _marker->set_position (adjusted_current_frame (event, false));
3338
3339         show_verbose_cursor_time (_real_section->frame());
3340 }
3341
3342 void
3343 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3344 {
3345         if (!movement_occurred) {
3346                 if (was_double_click()) {
3347                         _editor->edit_meter_marker (*_marker);
3348                 }
3349                 return;
3350         }
3351
3352         /* reinstate old snap setting */
3353         _editor->set_snap_to (_old_snap_type);
3354         _editor->set_snap_mode (_old_snap_mode);
3355
3356         TempoMap& map (_editor->session()->tempo_map());
3357
3358         XMLNode &after = map.get_state();
3359         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3360         _editor->commit_reversible_command ();
3361
3362         // delete the dummy marker we used for visual representation while moving.
3363         // a new visual marker will show up automatically.
3364         delete _marker;
3365 }
3366
3367 void
3368 MeterMarkerDrag::aborted (bool moved)
3369 {
3370         _marker->set_position (_marker->meter().frame ());
3371         if (moved) {
3372                 /* reinstate old snap setting */
3373                 _editor->set_snap_to (_old_snap_type);
3374                 _editor->set_snap_mode (_old_snap_mode);
3375
3376                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3377                 // delete the dummy marker we used for visual representation while moving.
3378                 // a new visual marker will show up automatically.
3379                 delete _marker;
3380         }
3381 }
3382
3383 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3384         : Drag (e, i)
3385         , _copy (c)
3386         , _grab_bpm (120.0, 4.0)
3387         , _grab_qn (0.0)
3388         , _before_state (0)
3389 {
3390         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3391
3392         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3393         _real_section = &_marker->tempo();
3394         _movable = !_real_section->initial();
3395         _grab_bpm = Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), _real_section->end_note_types_per_minute());
3396         _grab_qn = _real_section->pulse() * 4.0;
3397         assert (_marker);
3398 }
3399
3400 void
3401 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3402 {
3403         Drag::start_grab (event, cursor);
3404         if (!_real_section->active()) {
3405                 show_verbose_cursor_text (_("inactive"));
3406         } else {
3407                 show_verbose_cursor_time (adjusted_current_frame (event));
3408         }
3409 }
3410
3411 void
3412 TempoMarkerDrag::setup_pointer_frame_offset ()
3413 {
3414         _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3415 }
3416
3417 void
3418 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3419 {
3420         if (!_real_section->active()) {
3421                 return;
3422         }
3423         TempoMap& map (_editor->session()->tempo_map());
3424
3425         if (first_move) {
3426
3427                 // mvc drag - create a dummy marker to catch events, hide it.
3428
3429                 char name[64];
3430                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().note_types_per_minute());
3431
3432                 TempoSection section (_marker->tempo());
3433
3434                 _marker = new TempoMarker (
3435                         *_editor,
3436                         *_editor->tempo_group,
3437                         UIConfiguration::instance().color ("tempo marker"),
3438                         name,
3439                         *new TempoSection (_marker->tempo())
3440                         );
3441
3442                 /* use the new marker for the grab */
3443                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3444                 _marker->hide();
3445
3446                 /* get current state */
3447                 _before_state = &map.get_state();
3448
3449                 if (!_copy) {
3450                         _editor->begin_reversible_command (_("move tempo mark"));
3451
3452                 } else {
3453                         const Tempo tempo (_marker->tempo());
3454                         const framepos_t frame = adjusted_current_frame (event) + 1;
3455
3456                         _editor->begin_reversible_command (_("copy tempo mark"));
3457
3458                         if (_real_section->position_lock_style() == MusicTime) {
3459                                 const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
3460                                 _real_section = map.add_tempo (tempo, map.exact_qn_at_frame (frame, divisions), 0, MusicTime);
3461                         } else {
3462                                 _real_section = map.add_tempo (tempo, 0.0, frame, AudioTime);
3463                         }
3464
3465                         if (!_real_section) {
3466                                 aborted (true);
3467                                 return;
3468                         }
3469                 }
3470
3471         }
3472         if (ArdourKeyboard::indicates_constraint (event->button.state) && ArdourKeyboard::indicates_copy (event->button.state)) {
3473                 double new_bpm = max (1.5, _grab_bpm.end_note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3474                 stringstream strs;
3475                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (_real_section->note_types_per_minute(), _real_section->note_type(), new_bpm));
3476                 strs << "end:" << fixed << setprecision(3) << new_bpm;
3477                 show_verbose_cursor_text (strs.str());
3478
3479         } else if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3480                 /* use vertical movement to alter tempo .. should be log */
3481                 double new_bpm = max (1.5, _grab_bpm.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3482                 stringstream strs;
3483                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type(), _real_section->end_note_types_per_minute()));
3484                 strs << "start:" << fixed << setprecision(3) << new_bpm;
3485                 show_verbose_cursor_text (strs.str());
3486
3487         } else if (_movable && !_real_section->locked_to_meter()) {
3488                 framepos_t pf;
3489
3490                 if (_editor->snap_musical()) {
3491                         /* we can't snap to a grid that we are about to move.
3492                          * gui_move_tempo() will sort out snap using the supplied beat divisions.
3493                         */
3494                         pf = adjusted_current_frame (event, false);
3495                 } else {
3496                         pf = adjusted_current_frame (event);
3497                 }
3498
3499                 /* snap to beat is 1, snap to bar is -1 (sorry) */
3500                 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3501
3502                 map.gui_set_tempo_position (_real_section, pf, sub_num);
3503
3504                 show_verbose_cursor_time (_real_section->frame());
3505         }
3506         _marker->set_position (adjusted_current_frame (event, false));
3507 }
3508
3509 void
3510 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3511 {
3512         if (!_real_section->active()) {
3513                 return;
3514         }
3515         if (!movement_occurred) {
3516                 if (was_double_click()) {
3517                         _editor->edit_tempo_marker (*_marker);
3518                 }
3519                 return;
3520         }
3521
3522         TempoMap& map (_editor->session()->tempo_map());
3523
3524         XMLNode &after = map.get_state();
3525         _editor->session()->add_command (new MementoCommand<TempoMap>(map, _before_state, &after));
3526         _editor->commit_reversible_command ();
3527
3528         // delete the dummy marker we used for visual representation while moving.
3529         // a new visual marker will show up automatically.
3530         delete _marker;
3531 }
3532
3533 void
3534 TempoMarkerDrag::aborted (bool moved)
3535 {
3536         _marker->set_position (_marker->tempo().frame());
3537         if (moved) {
3538                 TempoMap& map (_editor->session()->tempo_map());
3539                 map.set_state (*_before_state, Stateful::current_state_version);
3540                 // delete the dummy (hidden) marker we used for events while moving.
3541                 delete _marker;
3542         }
3543 }
3544
3545 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3546         : Drag (e, i)
3547         , _grab_qn (0.0)
3548         , _tempo (0)
3549         , _before_state (0)
3550         , _drag_valid (true)
3551 {
3552         DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3553
3554 }
3555
3556 void
3557 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3558 {
3559         Drag::start_grab (event, cursor);
3560         TempoMap& map (_editor->session()->tempo_map());
3561         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3562
3563         if (adjusted_current_frame (event, false) <= _tempo->frame()) {
3564                 _drag_valid = false;
3565                 return;
3566         }
3567
3568         _editor->tempo_curve_selected (_tempo, true);
3569
3570         ostringstream sstr;
3571         if (_tempo->clamped()) {
3572                 TempoSection* prev = map.previous_tempo_section (_tempo);
3573                 if (prev) {
3574                         sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3575                 }
3576         }
3577
3578         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3579         show_verbose_cursor_text (sstr.str());
3580 }
3581
3582 void
3583 BBTRulerDrag::setup_pointer_frame_offset ()
3584 {
3585         TempoMap& map (_editor->session()->tempo_map());
3586         /* get current state */
3587         _before_state = &map.get_state();
3588
3589         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3590         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3591         double beat = 0.0;
3592
3593         if (divisions > 0) {
3594                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3595         } else {
3596                 /* while it makes some sense for the user to determine the division to 'grab',
3597                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3598                    and the result over steep tempo curves. Use sixteenths.
3599                 */
3600                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3601         }
3602
3603         _grab_qn = map.quarter_note_at_beat (beat);
3604
3605         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3606
3607 }
3608
3609 void
3610 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3611 {
3612         if (!_drag_valid) {
3613                 return;
3614         }
3615
3616         if (first_move) {
3617                 _editor->begin_reversible_command (_("stretch tempo"));
3618         }
3619
3620         TempoMap& map (_editor->session()->tempo_map());
3621         framepos_t pf;
3622
3623         if (_editor->snap_musical()) {
3624                 pf = adjusted_current_frame (event, false);
3625         } else {
3626                 pf = adjusted_current_frame (event);
3627         }
3628
3629         if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3630                 /* adjust previous tempo to match pointer frame */
3631                 _editor->session()->tempo_map().gui_stretch_tempo (_tempo, map.frame_at_quarter_note (_grab_qn), pf, _grab_qn, map.quarter_note_at_frame (pf));
3632         }
3633
3634         ostringstream sstr;
3635         if (_tempo->clamped()) {
3636                 TempoSection* prev = map.previous_tempo_section (_tempo);
3637                 if (prev) {
3638                         _editor->tempo_curve_selected (prev, true);
3639                         sstr << "end: " << fixed << setprecision(3) << prev->end_note_types_per_minute() << "\n";
3640                 }
3641         }
3642         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3643         show_verbose_cursor_text (sstr.str());
3644 }
3645
3646 void
3647 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3648 {
3649         if (!movement_occurred) {
3650                 return;
3651         }
3652
3653         TempoMap& map (_editor->session()->tempo_map());
3654
3655         _editor->tempo_curve_selected (_tempo, false);
3656         if (_tempo->clamped()) {
3657                 TempoSection* prev_tempo = map.previous_tempo_section (_tempo);
3658                 if (prev_tempo) {
3659                         _editor->tempo_curve_selected (prev_tempo, false);
3660                 }
3661         }
3662
3663         if (!movement_occurred || !_drag_valid) {
3664                 return;
3665         }
3666
3667         XMLNode &after = map.get_state();
3668         _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3669         _editor->commit_reversible_command ();
3670
3671 }
3672
3673 void
3674 BBTRulerDrag::aborted (bool moved)
3675 {
3676         if (moved) {
3677                 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3678         }
3679 }
3680
3681 TempoTwistDrag::TempoTwistDrag (Editor* e, ArdourCanvas::Item* i)
3682         : Drag (e, i)
3683         , _grab_qn (0.0)
3684         , _grab_tempo (0.0)
3685         , _tempo (0)
3686         , _next_tempo (0)
3687         , _drag_valid (true)
3688         , _before_state (0)
3689 {
3690         DEBUG_TRACE (DEBUG::Drags, "New TempoTwistDrag\n");
3691
3692 }
3693
3694 void
3695 TempoTwistDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3696 {
3697         Drag::start_grab (event, cursor);
3698         TempoMap& map (_editor->session()->tempo_map());
3699         /* get current state */
3700         _before_state = &map.get_state();
3701         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3702
3703         if (_tempo->locked_to_meter()) {
3704                 _drag_valid = false;
3705                 return;
3706         }
3707
3708         _next_tempo = map.next_tempo_section (_tempo);
3709         if (_next_tempo) {
3710                 if (!map.next_tempo_section (_next_tempo)) {
3711                         _drag_valid = false;
3712                         finished (event, false);
3713
3714                         return;
3715                 }
3716                 _editor->tempo_curve_selected (_tempo, true);
3717                 _editor->tempo_curve_selected (_next_tempo, true);
3718
3719                 ostringstream sstr;
3720                 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3721                 sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3722                 sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3723                 show_verbose_cursor_text (sstr.str());
3724         } else {
3725                 _drag_valid = false;
3726         }
3727
3728         _grab_tempo = Tempo (_tempo->note_types_per_minute(), _tempo->note_type());
3729 }
3730
3731 void
3732 TempoTwistDrag::setup_pointer_frame_offset ()
3733 {
3734         TempoMap& map (_editor->session()->tempo_map());
3735         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3736         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3737         double beat = 0.0;
3738
3739         if (divisions > 0) {
3740                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3741         } else {
3742                 /* while it makes some sense for the user to determine the division to 'grab',
3743                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3744                    and the result over steep tempo curves. Use sixteenths.
3745                 */
3746                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3747         }
3748
3749         _grab_qn = map.quarter_note_at_beat (beat);
3750
3751         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3752
3753 }
3754
3755 void
3756 TempoTwistDrag::motion (GdkEvent* event, bool first_move)
3757 {
3758
3759         if (!_next_tempo || !_drag_valid) {
3760                 return;
3761         }
3762
3763         TempoMap& map (_editor->session()->tempo_map());
3764
3765         if (first_move) {
3766                 _editor->begin_reversible_command (_("twist tempo"));
3767         }
3768
3769         framepos_t pf;
3770
3771         if (_editor->snap_musical()) {
3772                 pf = adjusted_current_frame (event, false);
3773         } else {
3774                 pf = adjusted_current_frame (event);
3775         }
3776
3777         /* adjust this and the next tempi to match pointer frame */
3778         double new_bpm = max (1.5, _grab_tempo.note_types_per_minute() + ((grab_y() - min (-1.0, current_pointer_y())) / 5.0));
3779         _editor->session()->tempo_map().gui_twist_tempi (_tempo, new_bpm, map.frame_at_quarter_note (_grab_qn), pf);
3780
3781         ostringstream sstr;
3782         sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute() << "\n";
3783         sstr << "end: " << fixed << setprecision(3) << _tempo->end_note_types_per_minute() << "\n";
3784         sstr << "start: " << fixed << setprecision(3) << _next_tempo->note_types_per_minute();
3785         show_verbose_cursor_text (sstr.str());
3786 }
3787
3788 void
3789 TempoTwistDrag::finished (GdkEvent* event, bool movement_occurred)
3790 {
3791         if (!movement_occurred || !_drag_valid) {
3792                 return;
3793         }
3794
3795         _editor->tempo_curve_selected (_tempo, false);
3796         _editor->tempo_curve_selected (_next_tempo, false);
3797
3798         TempoMap& map (_editor->session()->tempo_map());
3799         XMLNode &after = map.get_state();
3800         _editor->session()->add_command(new MementoCommand<TempoMap>(map, _before_state, &after));
3801         _editor->commit_reversible_command ();
3802 }
3803
3804 void
3805 TempoTwistDrag::aborted (bool moved)
3806 {
3807         if (moved) {
3808                 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3809         }
3810 }
3811
3812 TempoEndDrag::TempoEndDrag (Editor* e, ArdourCanvas::Item* i)
3813         : Drag (e, i)
3814         , _grab_qn (0.0)
3815         , _tempo (0)
3816         , _before_state (0)
3817         , _drag_valid (true)
3818 {
3819         DEBUG_TRACE (DEBUG::Drags, "New TempoEndDrag\n");
3820         TempoMarker* marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3821         _tempo = &marker->tempo();
3822         _grab_qn = _tempo->pulse() * 4.0;
3823 }
3824
3825 void
3826 TempoEndDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3827 {
3828         Drag::start_grab (event, cursor);
3829         TempoMap& tmap (_editor->session()->tempo_map());
3830
3831         /* get current state */
3832         _before_state = &tmap.get_state();
3833
3834         if (_tempo->locked_to_meter()) {
3835                 _drag_valid = false;
3836                 return;
3837         }
3838
3839         ostringstream sstr;
3840
3841         TempoSection* prev = 0;
3842         if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3843                 _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
3844                 sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3845         }
3846
3847         if (_tempo->clamped()) {
3848                 _editor->tempo_curve_selected (_tempo, true);
3849                 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3850         }
3851
3852         show_verbose_cursor_text (sstr.str());
3853 }
3854
3855 void
3856 TempoEndDrag::setup_pointer_frame_offset ()
3857 {
3858         TempoMap& map (_editor->session()->tempo_map());
3859
3860         _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
3861
3862 }
3863
3864 void
3865 TempoEndDrag::motion (GdkEvent* event, bool first_move)
3866 {
3867         if (!_drag_valid) {
3868                 return;
3869         }
3870
3871         TempoMap& map (_editor->session()->tempo_map());
3872
3873         if (first_move) {
3874                 _editor->begin_reversible_command (_("stretch end tempo"));
3875         }
3876
3877         framepos_t const pf = adjusted_current_frame (event, false);
3878         map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
3879
3880         ostringstream sstr;
3881         sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
3882
3883         if (_tempo->clamped()) {
3884                 sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
3885         }
3886
3887         show_verbose_cursor_text (sstr.str());
3888 }
3889
3890 void
3891 TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
3892 {
3893         if (!movement_occurred || !_drag_valid) {
3894                 return;
3895         }
3896
3897         TempoMap& tmap (_editor->session()->tempo_map());
3898
3899         XMLNode &after = tmap.get_state();
3900         _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
3901         _editor->commit_reversible_command ();
3902
3903         TempoSection* prev = 0;
3904         if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
3905                 _editor->tempo_curve_selected (prev, false);
3906         }
3907
3908         if (_tempo->clamped()) {
3909                 _editor->tempo_curve_selected (_tempo, false);
3910
3911         }
3912 }
3913
3914 void
3915 TempoEndDrag::aborted (bool moved)
3916 {
3917         if (moved) {
3918                 _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
3919         }
3920 }
3921
3922 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3923         : Drag (e, &c.track_canvas_item(), false)
3924         , _cursor (c)
3925         , _stop (s)
3926         , _grab_zoom (0.0)
3927 {
3928         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3929 }
3930
3931 /** Do all the things we do when dragging the playhead to make it look as though
3932  *  we have located, without actually doing the locate (because that would cause
3933  *  the diskstream buffers to be refilled, which is too slow).
3934  */
3935 void
3936 CursorDrag::fake_locate (framepos_t t)
3937 {
3938         if (_editor->session () == 0) {
3939                 return;
3940         }
3941
3942         _editor->playhead_cursor->set_position (t);
3943
3944         Session* s = _editor->session ();
3945         if (s->timecode_transmission_suspended ()) {
3946                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3947                 /* This is asynchronous so it will be sent "now"
3948                  */
3949                 s->send_mmc_locate (f);
3950                 /* These are synchronous and will be sent during the next
3951                    process cycle
3952                 */
3953                 s->queue_full_time_code ();
3954                 s->queue_song_position_pointer ();
3955         }
3956
3957         show_verbose_cursor_time (t);
3958         _editor->UpdateAllTransportClocks (t);
3959 }
3960
3961 void
3962 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3963 {
3964         Drag::start_grab (event, c);
3965         setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
3966
3967         _grab_zoom = _editor->samples_per_pixel;
3968
3969         MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
3970
3971         _editor->snap_to_with_modifier (where, event);
3972         _editor->_dragging_playhead = true;
3973         _editor->_control_scroll_target = where.frame;
3974
3975         Session* s = _editor->session ();
3976
3977         /* grab the track canvas item as well */
3978
3979         _cursor.track_canvas_item().grab();
3980
3981         if (s) {
3982                 if (_was_rolling && _stop) {
3983                         s->request_stop ();
3984                 }
3985
3986                 if (s->is_auditioning()) {
3987                         s->cancel_audition ();
3988                 }
3989
3990
3991                 if (AudioEngine::instance()->running()) {
3992
3993                         /* do this only if we're the engine is connected
3994                          * because otherwise this request will never be
3995                          * serviced and we'll busy wait forever. likewise,
3996                          * notice if we are disconnected while waiting for the
3997                          * request to be serviced.
3998                          */
3999
4000                         s->request_suspend_timecode_transmission ();
4001                         while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
4002                                 /* twiddle our thumbs */
4003                         }
4004                 }
4005         }
4006
4007         fake_locate (where.frame - snap_delta (event->button.state));
4008         
4009         _last_y_delta = 0;
4010 }
4011
4012 void
4013 CursorDrag::motion (GdkEvent* event, bool)
4014 {
4015         MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4016
4017         _editor->snap_to_with_modifier (where, event);
4018
4019         if (where.frame != last_pointer_frame()) {
4020                 fake_locate (where.frame - snap_delta (event->button.state));
4021         }
4022         
4023         //maybe do zooming, too, if the option is enabled
4024         if (UIConfiguration::instance ().get_use_time_rulers_to_zoom_with_vertical_drag () ) {
4025         
4026                 //To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
4027                 //we use screen coordinates for this, not canvas-based grab_x
4028                 double mx = event->button.x;
4029                 double dx = fabs(mx - _last_mx);
4030                 double my = event->button.y;
4031                 double dy = fabs(my - _last_my);
4032
4033                 {
4034                         //do zooming in windowed "steps" so it feels more reversible ?
4035                         const int stepsize = 2;  //stepsize ==1  means "trigger on every pixel of movement"
4036                         int y_delta = grab_y() - current_pointer_y();
4037                         y_delta = y_delta / stepsize;
4038
4039                         //if all requirements are met, do the actual zoom
4040                         const double scale = 1.2;
4041                         if ( (dy>dx) && (_last_dx ==0) && (y_delta != _last_y_delta) ) {
4042                                 if ( _last_y_delta > y_delta ) {
4043                                         _editor->temporal_zoom_step_mouse_focus_scale (true, scale);
4044                                 } else {
4045                                         _editor->temporal_zoom_step_mouse_focus_scale (false, scale);
4046                                 }
4047                                 _last_y_delta = y_delta;
4048                         }
4049                 }
4050
4051                 _last_my = my;
4052                 _last_mx = mx;
4053                 _last_dx = dx;
4054         }
4055 }
4056
4057 void
4058 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
4059 {
4060         _editor->_dragging_playhead = false;
4061
4062         _cursor.track_canvas_item().ungrab();
4063
4064         if (!movement_occurred && _stop) {
4065                 return;
4066         }
4067
4068         motion (event, false);
4069
4070         Session* s = _editor->session ();
4071         if (s) {
4072                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
4073                 _editor->_pending_locate_request = true;
4074                 s->request_resume_timecode_transmission ();
4075         }
4076 }
4077
4078 void
4079 CursorDrag::aborted (bool)
4080 {
4081         _cursor.track_canvas_item().ungrab();
4082
4083         if (_editor->_dragging_playhead) {
4084                 _editor->session()->request_resume_timecode_transmission ();
4085                 _editor->_dragging_playhead = false;
4086         }
4087
4088         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false).frame);
4089 }
4090
4091 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4092         : RegionDrag (e, i, p, v)
4093 {
4094         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
4095 }
4096
4097 void
4098 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4099 {
4100         Drag::start_grab (event, cursor);
4101
4102         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4103         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4104         setup_snap_delta (MusicFrame (r->position(), 0));
4105
4106         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
4107 }
4108
4109 void
4110 FadeInDrag::setup_pointer_frame_offset ()
4111 {
4112         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4113         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
4114         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
4115 }
4116
4117 void
4118 FadeInDrag::motion (GdkEvent* event, bool)
4119 {
4120         framecnt_t fade_length;
4121
4122         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4123         _editor->snap_to_with_modifier (pos, event);
4124
4125         pos.frame -= snap_delta (event->button.state);
4126
4127         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4128
4129         if (pos.frame < (region->position() + 64)) {
4130                 fade_length = 64; // this should be a minimum defined somewhere
4131         } else if (pos.frame > region->position() + region->length() - region->fade_out()->back()->when) {
4132                 fade_length = region->length() - region->fade_out()->back()->when - 1;
4133         } else {
4134                 fade_length = pos.frame - region->position();
4135         }
4136
4137         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4138
4139                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4140
4141                 if (!tmp) {
4142                         continue;
4143                 }
4144
4145                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
4146         }
4147
4148         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
4149 }
4150
4151 void
4152 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
4153 {
4154         if (!movement_occurred) {
4155                 return;
4156         }
4157
4158         framecnt_t fade_length;
4159         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4160
4161         _editor->snap_to_with_modifier (pos, event);
4162         pos.frame -= snap_delta (event->button.state);
4163
4164         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4165
4166         if (pos.frame < (region->position() + 64)) {
4167                 fade_length = 64; // this should be a minimum defined somewhere
4168         } else if (pos.frame >= region->position() + region->length() - region->fade_out()->back()->when) {
4169                 fade_length = region->length() - region->fade_out()->back()->when - 1;
4170         } else {
4171                 fade_length = pos.frame - region->position();
4172         }
4173
4174         bool in_command = false;
4175
4176         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4177
4178                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4179
4180                 if (!tmp) {
4181                         continue;
4182                 }
4183
4184                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
4185                 XMLNode &before = alist->get_state();
4186
4187                 tmp->audio_region()->set_fade_in_length (fade_length);
4188                 tmp->audio_region()->set_fade_in_active (true);
4189
4190                 if (!in_command) {
4191                         _editor->begin_reversible_command (_("change fade in length"));
4192                         in_command = true;
4193                 }
4194                 XMLNode &after = alist->get_state();
4195                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4196         }
4197
4198         if (in_command) {
4199                 _editor->commit_reversible_command ();
4200         }
4201 }
4202
4203 void
4204 FadeInDrag::aborted (bool)
4205 {
4206         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4207                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4208
4209                 if (!tmp) {
4210                         continue;
4211                 }
4212
4213                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
4214         }
4215 }
4216
4217 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
4218         : RegionDrag (e, i, p, v)
4219 {
4220         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
4221 }
4222
4223 void
4224 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4225 {
4226         Drag::start_grab (event, cursor);
4227
4228         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4229         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4230         setup_snap_delta (MusicFrame (r->last_frame(), 0));
4231
4232         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
4233 }
4234
4235 void
4236 FadeOutDrag::setup_pointer_frame_offset ()
4237 {
4238         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
4239         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
4240         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
4241 }
4242
4243 void
4244 FadeOutDrag::motion (GdkEvent* event, bool)
4245 {
4246         framecnt_t fade_length;
4247         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4248
4249         _editor->snap_to_with_modifier (pos, event);
4250         pos.frame -= snap_delta (event->button.state);
4251
4252         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4253
4254         if (pos.frame > (region->last_frame() - 64)) {
4255                 fade_length = 64; // this should really be a minimum fade defined somewhere
4256         } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4257                 fade_length = region->length() - region->fade_in()->back()->when - 1;
4258         } else {
4259                 fade_length = region->last_frame() - pos.frame;
4260         }
4261
4262         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4263
4264                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4265
4266                 if (!tmp) {
4267                         continue;
4268                 }
4269
4270                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
4271         }
4272
4273         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
4274 }
4275
4276 void
4277 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
4278 {
4279         if (!movement_occurred) {
4280                 return;
4281         }
4282
4283         framecnt_t fade_length;
4284         MusicFrame pos (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
4285
4286         _editor->snap_to_with_modifier (pos, event);
4287         pos.frame -= snap_delta (event->button.state);
4288
4289         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
4290
4291         if (pos.frame > (region->last_frame() - 64)) {
4292                 fade_length = 64; // this should really be a minimum fade defined somewhere
4293         } else if (pos.frame <= region->position() + region->fade_in()->back()->when) {
4294                 fade_length = region->length() - region->fade_in()->back()->when - 1;
4295         } else {
4296                 fade_length = region->last_frame() - pos.frame;
4297         }
4298
4299         bool in_command = false;
4300
4301         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4302
4303                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4304
4305                 if (!tmp) {
4306                         continue;
4307                 }
4308
4309                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
4310                 XMLNode &before = alist->get_state();
4311
4312                 tmp->audio_region()->set_fade_out_length (fade_length);
4313                 tmp->audio_region()->set_fade_out_active (true);
4314
4315                 if (!in_command) {
4316                         _editor->begin_reversible_command (_("change fade out length"));
4317                         in_command = true;
4318                 }
4319                 XMLNode &after = alist->get_state();
4320                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
4321         }
4322
4323         if (in_command) {
4324                 _editor->commit_reversible_command ();
4325         }
4326 }
4327
4328 void
4329 FadeOutDrag::aborted (bool)
4330 {
4331         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
4332                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
4333
4334                 if (!tmp) {
4335                         continue;
4336                 }
4337
4338                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
4339         }
4340 }
4341
4342 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
4343         : Drag (e, i)
4344         , _selection_changed (false)
4345 {
4346         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
4347         Gtk::Window* toplevel = _editor->current_toplevel();
4348         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
4349
4350         assert (_marker);
4351
4352         _points.push_back (ArdourCanvas::Duple (0, 0));
4353
4354         _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
4355 }
4356
4357 MarkerDrag::~MarkerDrag ()
4358 {
4359         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
4360                 delete i->location;
4361         }
4362 }
4363
4364 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
4365 {
4366         location = new Location (*l);
4367         markers.push_back (m);
4368         move_both = false;
4369 }
4370
4371 void
4372 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4373 {
4374         Drag::start_grab (event, cursor);
4375
4376         bool is_start;
4377
4378         Location *location = _editor->find_location_from_marker (_marker, is_start);
4379         _editor->_dragging_edit_point = true;
4380
4381         update_item (location);
4382
4383         // _drag_line->show();
4384         // _line->raise_to_top();
4385
4386         if (is_start) {
4387                 show_verbose_cursor_time (location->start());
4388         } else {
4389                 show_verbose_cursor_time (location->end());
4390         }
4391         setup_snap_delta (MusicFrame (is_start ? location->start() : location->end(), 0));
4392
4393         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4394
4395         switch (op) {
4396         case Selection::Toggle:
4397                 /* we toggle on the button release */
4398                 break;
4399         case Selection::Set:
4400                 if (!_editor->selection->selected (_marker)) {
4401                         _editor->selection->set (_marker);
4402                         _selection_changed = true;
4403                 }
4404                 break;
4405         case Selection::Extend:
4406         {
4407                 Locations::LocationList ll;
4408                 list<ArdourMarker*> to_add;
4409                 framepos_t s, e;
4410                 _editor->selection->markers.range (s, e);
4411                 s = min (_marker->position(), s);
4412                 e = max (_marker->position(), e);
4413                 s = min (s, e);
4414                 e = max (s, e);
4415                 if (e < max_framepos) {
4416                         ++e;
4417                 }
4418                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
4419                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
4420                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
4421                         if (lm) {
4422                                 if (lm->start) {
4423                                         to_add.push_back (lm->start);
4424                                 }
4425                                 if (lm->end) {
4426                                         to_add.push_back (lm->end);
4427                                 }
4428                         }
4429                 }
4430                 if (!to_add.empty()) {
4431                         _editor->selection->add (to_add);
4432                         _selection_changed = true;
4433                 }
4434                 break;
4435         }
4436         case Selection::Add:
4437                 _editor->selection->add (_marker);
4438                 _selection_changed = true;
4439
4440                 break;
4441         }
4442
4443         /* Set up copies for us to manipulate during the drag
4444          */
4445
4446         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4447
4448                 Location* l = _editor->find_location_from_marker (*i, is_start);
4449
4450                 if (!l) {
4451                         continue;
4452                 }
4453
4454                 if (l->is_mark()) {
4455                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4456                 } else {
4457                         /* range: check that the other end of the range isn't
4458                            already there.
4459                         */
4460                         CopiedLocationInfo::iterator x;
4461                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4462                                 if (*(*x).location == *l) {
4463                                         break;
4464                                 }
4465                         }
4466                         if (x == _copied_locations.end()) {
4467                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4468                         } else {
4469                                 (*x).markers.push_back (*i);
4470                                 (*x).move_both = true;
4471                         }
4472                 }
4473
4474         }
4475 }
4476
4477 void
4478 MarkerDrag::setup_pointer_frame_offset ()
4479 {
4480         bool is_start;
4481         Location *location = _editor->find_location_from_marker (_marker, is_start);
4482         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4483 }
4484
4485 void
4486 MarkerDrag::motion (GdkEvent* event, bool)
4487 {
4488         framecnt_t f_delta = 0;
4489         bool is_start;
4490         bool move_both = false;
4491         Location *real_location;
4492         Location *copy_location = 0;
4493         framecnt_t const sd = snap_delta (event->button.state);
4494
4495         framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true).frame - sd;
4496         framepos_t next = newframe;
4497
4498         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4499                 move_both = true;
4500         }
4501
4502         CopiedLocationInfo::iterator x;
4503
4504         /* find the marker we're dragging, and compute the delta */
4505
4506         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4507
4508                 copy_location = (*x).location;
4509
4510                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4511
4512                         /* this marker is represented by this
4513                          * CopiedLocationMarkerInfo
4514                          */
4515
4516                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4517                                 /* que pasa ?? */
4518                                 return;
4519                         }
4520
4521                         if (real_location->is_mark()) {
4522                                 f_delta = newframe - copy_location->start();
4523                         } else {
4524
4525
4526                                 switch (_marker->type()) {
4527                                 case ArdourMarker::SessionStart:
4528                                 case ArdourMarker::RangeStart:
4529                                 case ArdourMarker::LoopStart:
4530                                 case ArdourMarker::PunchIn:
4531                                         f_delta = newframe - copy_location->start();
4532                                         break;
4533
4534                                 case ArdourMarker::SessionEnd:
4535                                 case ArdourMarker::RangeEnd:
4536                                 case ArdourMarker::LoopEnd:
4537                                 case ArdourMarker::PunchOut:
4538                                         f_delta = newframe - copy_location->end();
4539                                         break;
4540                                 default:
4541                                         /* what kind of marker is this ? */
4542                                         return;
4543                                 }
4544                         }
4545
4546                         break;
4547                 }
4548         }
4549
4550         if (x == _copied_locations.end()) {
4551                 /* hmm, impossible - we didn't find the dragged marker */
4552                 return;
4553         }
4554
4555         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4556
4557         /* now move them all */
4558
4559         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4560
4561                 copy_location = x->location;
4562
4563                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4564                         continue;
4565                 }
4566
4567                 if (real_location->locked()) {
4568                         continue;
4569                 }
4570
4571                 if (copy_location->is_mark()) {
4572
4573                         /* now move it */
4574                         copy_location->set_start (copy_location->start() + f_delta, false, true, divisions);
4575
4576                 } else {
4577
4578                         framepos_t new_start = copy_location->start() + f_delta;
4579                         framepos_t new_end = copy_location->end() + f_delta;
4580
4581                         if (is_start) { // start-of-range marker
4582
4583                                 if (move_both || (*x).move_both) {
4584                                         copy_location->set_start (new_start, false, true, divisions);
4585                                         copy_location->set_end (new_end, false, true, divisions);
4586                                 } else  if (new_start < copy_location->end()) {
4587                                         copy_location->set_start (new_start, false, true, divisions);
4588                                 } else if (newframe > 0) {
4589                                         //_editor->snap_to (next, RoundUpAlways, true);
4590                                         copy_location->set_end (next, false, true, divisions);
4591                                         copy_location->set_start (newframe, false, true, divisions);
4592                                 }
4593
4594                         } else { // end marker
4595
4596                                 if (move_both || (*x).move_both) {
4597                                         copy_location->set_end (new_end, divisions);
4598                                         copy_location->set_start (new_start, false, true, divisions);
4599                                 } else if (new_end > copy_location->start()) {
4600                                         copy_location->set_end (new_end, false, true, divisions);
4601                                 } else if (newframe > 0) {
4602                                         //_editor->snap_to (next, RoundDownAlways, true);
4603                                         copy_location->set_start (next, false, true, divisions);
4604                                         copy_location->set_end (newframe, false, true, divisions);
4605                                 }
4606                         }
4607                 }
4608
4609                 update_item (copy_location);
4610
4611                 /* now lookup the actual GUI items used to display this
4612                  * location and move them to wherever the copy of the location
4613                  * is now. This means that the logic in ARDOUR::Location is
4614                  * still enforced, even though we are not (yet) modifying
4615                  * the real Location itself.
4616                  */
4617
4618                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4619
4620                 if (lm) {
4621                         lm->set_position (copy_location->start(), copy_location->end());
4622                 }
4623
4624         }
4625
4626         assert (!_copied_locations.empty());
4627
4628         show_verbose_cursor_time (newframe);
4629 }
4630
4631 void
4632 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4633 {
4634         if (!movement_occurred) {
4635
4636                 if (was_double_click()) {
4637                         _editor->rename_marker (_marker);
4638                         return;
4639                 }
4640
4641                 /* just a click, do nothing but finish
4642                    off the selection process
4643                 */
4644
4645                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4646                 switch (op) {
4647                 case Selection::Set:
4648                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4649                                 _editor->selection->set (_marker);
4650                                 _selection_changed = true;
4651                         }
4652                         break;
4653
4654                 case Selection::Toggle:
4655                         /* we toggle on the button release, click only */
4656                         _editor->selection->toggle (_marker);
4657                         _selection_changed = true;
4658
4659                         break;
4660
4661                 case Selection::Extend:
4662                 case Selection::Add:
4663                         break;
4664                 }
4665
4666                 if (_selection_changed) {
4667                         _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4668                         _editor->commit_reversible_selection_op();
4669                 }
4670
4671                 return;
4672         }
4673
4674         _editor->_dragging_edit_point = false;
4675
4676         XMLNode &before = _editor->session()->locations()->get_state();
4677         bool in_command = false;
4678
4679         MarkerSelection::iterator i;
4680         CopiedLocationInfo::iterator x;
4681         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
4682         bool is_start;
4683
4684         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4685              x != _copied_locations.end() && i != _editor->selection->markers.end();
4686              ++i, ++x) {
4687
4688                 Location * location = _editor->find_location_from_marker (*i, is_start);
4689
4690                 if (location) {
4691
4692                         if (location->locked()) {
4693                                 continue;
4694                         }
4695                         if (!in_command) {
4696                                 _editor->begin_reversible_command ( _("move marker") );
4697                                 in_command = true;
4698                         }
4699                         if (location->is_mark()) {
4700                                 location->set_start (((*x).location)->start(), false, true, divisions);
4701                         } else {
4702                                 location->set (((*x).location)->start(), ((*x).location)->end(), true, divisions);
4703                         }
4704
4705                         if (location->is_session_range()) {
4706                                 _editor->session()->set_end_is_free (false);
4707                         }
4708                 }
4709         }
4710
4711         if (in_command) {
4712                 XMLNode &after = _editor->session()->locations()->get_state();
4713                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4714                 _editor->commit_reversible_command ();
4715         }
4716 }
4717
4718 void
4719 MarkerDrag::aborted (bool movement_occurred)
4720 {
4721         if (!movement_occurred) {
4722                 return;
4723         }
4724
4725         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4726
4727                 /* move all markers to their original location */
4728
4729
4730                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4731
4732                         bool is_start;
4733                         Location * location = _editor->find_location_from_marker (*m, is_start);
4734
4735                         if (location) {
4736                                 (*m)->set_position (is_start ? location->start() : location->end());
4737                         }
4738                 }
4739         }
4740 }
4741
4742 void
4743 MarkerDrag::update_item (Location*)
4744 {
4745         /* noop */
4746 }
4747
4748 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4749         : Drag (e, i)
4750         , _fixed_grab_x (0.0)
4751         , _fixed_grab_y (0.0)
4752         , _cumulative_x_drag (0.0)
4753         , _cumulative_y_drag (0.0)
4754         , _pushing (false)
4755         , _final_index (0)
4756 {
4757         if (_zero_gain_fraction < 0.0) {
4758                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4759         }
4760
4761         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4762
4763         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4764         assert (_point);
4765 }
4766
4767
4768 void
4769 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4770 {
4771         Drag::start_grab (event, _editor->cursors()->fader);
4772
4773         // start the grab at the center of the control point so
4774         // the point doesn't 'jump' to the mouse after the first drag
4775         _fixed_grab_x = _point->get_x() + _editor->sample_to_pixel_unrounded (_point->line().offset());
4776         _fixed_grab_y = _point->get_y();
4777
4778         setup_snap_delta (MusicFrame (_editor->pixel_to_sample (_fixed_grab_x), 0));
4779
4780         float const fraction = 1 - (_point->get_y() / _point->line().height());
4781         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4782
4783         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4784
4785         if (!_point->can_slide ()) {
4786                 _x_constrained = true;
4787         }
4788 }
4789
4790 void
4791 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4792 {
4793         double dx = _drags->current_pointer_x() - last_pointer_x();
4794         double dy = current_pointer_y() - last_pointer_y();
4795         bool need_snap = true;
4796
4797         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4798                 dx *= 0.1;
4799                 dy *= 0.1;
4800                 need_snap = false;
4801         }
4802
4803         /* coordinate in pixels relative to the start of the region (for region-based automation)
4804            or track (for track-based automation) */
4805         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4806         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4807
4808         // calculate zero crossing point. back off by .01 to stay on the
4809         // positive side of zero
4810         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4811
4812         if (_x_constrained) {
4813                 cx = _fixed_grab_x;
4814         }
4815         if (_y_constrained) {
4816                 cy = _fixed_grab_y;
4817         }
4818
4819         _cumulative_x_drag = cx - _fixed_grab_x;
4820         _cumulative_y_drag = cy - _fixed_grab_y;
4821
4822         cx = max (0.0, cx);
4823         cy = max (0.0, cy);
4824         cy = min ((double) _point->line().height(), cy);
4825
4826         // make sure we hit zero when passing through
4827         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4828                 cy = zero_gain_y;
4829         }
4830
4831         MusicFrame cx_mf (_editor->pixel_to_sample (cx) + snap_delta (event->button.state), 0);
4832
4833         if (!_x_constrained && need_snap) {
4834                 _editor->snap_to_with_modifier (cx_mf, event);
4835         }
4836
4837         cx_mf.frame -= snap_delta (event->button.state);
4838         cx_mf.frame = min (cx_mf.frame, _point->line().maximum_time() + _point->line().offset());
4839
4840         float const fraction = 1.0 - (cy / _point->line().height());
4841
4842         if (first_motion) {
4843                 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4844                 _editor->begin_reversible_command (_("automation event move"));
4845                 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4846         }
4847         pair<float, float> result;
4848         result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_mf.frame), fraction, false, _pushing, _final_index);
4849         show_verbose_cursor_text (_point->line().get_verbose_cursor_relative_string (result.first, result.second));
4850 }
4851
4852 void
4853 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4854 {
4855         if (!movement_occurred) {
4856
4857                 /* just a click */
4858                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4859                         _editor->reset_point_selection ();
4860                 }
4861
4862         } else {
4863                 _point->line().end_drag (_pushing, _final_index);
4864                 _editor->commit_reversible_command ();
4865         }
4866 }
4867
4868 void
4869 ControlPointDrag::aborted (bool)
4870 {
4871         _point->line().reset ();
4872 }
4873
4874 bool
4875 ControlPointDrag::active (Editing::MouseMode m)
4876 {
4877         if (m == Editing::MouseDraw) {
4878                 /* always active in mouse draw */
4879                 return true;
4880         }
4881
4882         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4883         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4884 }
4885
4886 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4887         : Drag (e, i)
4888         , _line (0)
4889         , _fixed_grab_x (0.0)
4890         , _fixed_grab_y (0.0)
4891         , _cumulative_y_drag (0)
4892         , _before (0)
4893         , _after (0)
4894 {
4895         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4896 }
4897
4898 void
4899 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4900 {
4901         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4902         assert (_line);
4903
4904         _item = &_line->grab_item ();
4905
4906         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4907            origin, and ditto for y.
4908         */
4909
4910         double mx = event->button.x;
4911         double my = event->button.y;
4912
4913         _line->grab_item().canvas_to_item (mx, my);
4914
4915         framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4916
4917         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4918                 /* no adjacent points */
4919                 return;
4920         }
4921
4922         Drag::start_grab (event, _editor->cursors()->fader);
4923
4924         /* store grab start in item frame */
4925         double const bx = _line->nth (_before)->get_x();
4926         double const ax = _line->nth (_after)->get_x();
4927         double const click_ratio = (ax - mx) / (ax - bx);
4928
4929         double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4930
4931         _fixed_grab_x = mx;
4932         _fixed_grab_y = cy;
4933
4934         double fraction = 1.0 - (cy / _line->height());
4935
4936         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4937 }
4938
4939 void
4940 LineDrag::motion (GdkEvent* event, bool first_move)
4941 {
4942         double dy = current_pointer_y() - last_pointer_y();
4943
4944         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4945                 dy *= 0.1;
4946         }
4947
4948         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4949
4950         _cumulative_y_drag = cy - _fixed_grab_y;
4951
4952         cy = max (0.0, cy);
4953         cy = min ((double) _line->height(), cy);
4954
4955         double const fraction = 1.0 - (cy / _line->height());
4956         uint32_t ignored;
4957
4958         if (first_move) {
4959                 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4960
4961                 _editor->begin_reversible_command (_("automation range move"));
4962                 _line->start_drag_line (_before, _after, initial_fraction);
4963         }
4964
4965         /* we are ignoring x position for this drag, so we can just pass in anything */
4966         pair<float, float> result;
4967
4968         result = _line->drag_motion (0, fraction, true, false, ignored);
4969         show_verbose_cursor_text (_line->get_verbose_cursor_relative_string (result.first, result.second));
4970 }
4971
4972 void
4973 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4974 {
4975         if (movement_occurred) {
4976                 motion (event, false);
4977                 _line->end_drag (false, 0);
4978                 _editor->commit_reversible_command ();
4979         } else {
4980                 /* add a new control point on the line */
4981
4982                 AutomationTimeAxisView* atv;
4983
4984                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4985                         framepos_t where = grab_frame ();
4986
4987                         double cx = 0;
4988                         double cy = _fixed_grab_y;
4989
4990                         _line->grab_item().item_to_canvas (cx, cy);
4991
4992                         atv->add_automation_event (event, where, cy, false);
4993                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4994                         AudioRegionView* arv;
4995
4996                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4997                                 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4998                         }
4999                 }
5000         }
5001 }
5002
5003 void
5004 LineDrag::aborted (bool)
5005 {
5006         _line->reset ();
5007 }
5008
5009 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
5010         : Drag (e, i),
5011           _line (0),
5012           _arv (0),
5013           _region_view_grab_x (0.0),
5014           _cumulative_x_drag (0),
5015           _before (0.0),
5016           _max_x (0)
5017 {
5018         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
5019 }
5020
5021 void
5022 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
5023 {
5024         Drag::start_grab (event);
5025
5026         _line = reinterpret_cast<Line*> (_item);
5027         assert (_line);
5028
5029         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
5030
5031         double cx = event->button.x;
5032         double cy = event->button.y;
5033
5034         _item->parent()->canvas_to_item (cx, cy);
5035
5036         /* store grab start in parent frame */
5037         _region_view_grab_x = cx;
5038
5039         _before = *(float*) _item->get_data ("position");
5040
5041         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
5042
5043         _max_x = _editor->sample_to_pixel(_arv->get_duration());
5044 }
5045
5046 void
5047 FeatureLineDrag::motion (GdkEvent*, bool)
5048 {
5049         double dx = _drags->current_pointer_x() - last_pointer_x();
5050
5051         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
5052
5053         _cumulative_x_drag += dx;
5054
5055         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
5056
5057         if (cx > _max_x){
5058                 cx = _max_x;
5059         }
5060         else if(cx < 0){
5061                 cx = 0;
5062         }
5063
5064         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
5065         assert (bbox);
5066         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
5067
5068         float *pos = new float;
5069         *pos = cx;
5070
5071         _line->set_data ("position", pos);
5072
5073         _before = cx;
5074 }
5075
5076 void
5077 FeatureLineDrag::finished (GdkEvent*, bool)
5078 {
5079         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
5080         _arv->update_transient(_before, _before);
5081 }
5082
5083 void
5084 FeatureLineDrag::aborted (bool)
5085 {
5086         //_line->reset ();
5087 }
5088
5089 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5090         : Drag (e, i)
5091         , _vertical_only (false)
5092 {
5093         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
5094 }
5095
5096 void
5097 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5098 {
5099         Drag::start_grab (event);
5100         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
5101 }
5102
5103 void
5104 RubberbandSelectDrag::motion (GdkEvent* event, bool)
5105 {
5106         framepos_t start;
5107         framepos_t end;
5108         double y1;
5109         double y2;
5110         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
5111         MusicFrame grab (grab_frame (), 0);
5112
5113         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5114                 _editor->snap_to_with_modifier (grab, event);
5115         } else {
5116                 grab.frame = raw_grab_frame ();
5117         }
5118
5119         /* base start and end on initial click position */
5120
5121         if (pf < grab.frame) {
5122                 start = pf;
5123                 end = grab.frame;
5124         } else {
5125                 end = pf;
5126                 start = grab.frame;
5127         }
5128
5129         if (current_pointer_y() < grab_y()) {
5130                 y1 = current_pointer_y();
5131                 y2 = grab_y();
5132         } else {
5133                 y2 = current_pointer_y();
5134                 y1 = grab_y();
5135         }
5136
5137         if (start != end || y1 != y2) {
5138
5139                 double x1 = _editor->sample_to_pixel (start);
5140                 double x2 = _editor->sample_to_pixel (end);
5141                 const double min_dimension = 2.0;
5142
5143                 if (_vertical_only) {
5144                         /* fixed 10 pixel width */
5145                         x2 = x1 + 10;
5146                 } else {
5147                         if (x2 < x1) {
5148                                 x2 = min (x1 - min_dimension, x2);
5149                         } else {
5150                                 x2 = max (x1 + min_dimension, x2);
5151                         }
5152                 }
5153
5154                 if (y2 < y1) {
5155                         y2 = min (y1 - min_dimension, y2);
5156                 } else {
5157                         y2 = max (y1 + min_dimension, y2);
5158                 }
5159
5160                 /* translate rect into item space and set */
5161
5162                 ArdourCanvas::Rect r (x1, y1, x2, y2);
5163
5164                 /* this drag is a _trackview_only == true drag, so the y1 and
5165                  * y2 (computed using current_pointer_y() and grab_y()) will be
5166                  * relative to the top of the trackview group). The
5167                  * rubberband rect has the same parent/scroll offset as the
5168                  * the trackview group, so we can use the "r" rect directly
5169                  * to set the shape of the rubberband.
5170                  */
5171
5172                 _editor->rubberband_rect->set (r);
5173                 _editor->rubberband_rect->show();
5174                 _editor->rubberband_rect->raise_to_top();
5175
5176                 show_verbose_cursor_time (pf);
5177
5178                 do_select_things (event, true);
5179         }
5180 }
5181
5182 void
5183 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
5184 {
5185         framepos_t x1;
5186         framepos_t x2;
5187         framepos_t grab = grab_frame ();
5188         framepos_t lpf = last_pointer_frame ();
5189
5190         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
5191                 grab = raw_grab_frame ();
5192                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
5193         }
5194
5195         if (grab < lpf) {
5196                 x1 = grab;
5197                 x2 = lpf;
5198         } else {
5199                 x2 = grab;
5200                 x1 = lpf;
5201         }
5202
5203         double y1;
5204         double y2;
5205
5206         if (current_pointer_y() < grab_y()) {
5207                 y1 = current_pointer_y();
5208                 y2 = grab_y();
5209         } else {
5210                 y2 = current_pointer_y();
5211                 y1 = grab_y();
5212         }
5213
5214         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
5215 }
5216
5217 void
5218 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
5219 {
5220         if (movement_occurred) {
5221
5222                 motion (event, false);
5223                 do_select_things (event, false);
5224
5225         } else {
5226
5227                 /* just a click */
5228
5229                 bool do_deselect = true;
5230                 MidiTimeAxisView* mtv;
5231
5232                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
5233                         /* MIDI track */
5234                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
5235                                 /* nothing selected */
5236                                 add_midi_region (mtv, true);
5237                                 do_deselect = false;
5238                         }
5239                 }
5240
5241                 /* do not deselect if Primary or Tertiary (toggle-select or
5242                  * extend-select are pressed.
5243                  */
5244
5245                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
5246                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
5247                     do_deselect) {
5248                         deselect_things ();
5249                 }
5250
5251         }
5252
5253         _editor->rubberband_rect->hide();
5254 }
5255
5256 void
5257 RubberbandSelectDrag::aborted (bool)
5258 {
5259         _editor->rubberband_rect->hide ();
5260 }
5261
5262 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
5263         : RegionDrag (e, i, p, v)
5264 {
5265         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
5266 }
5267
5268 void
5269 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5270 {
5271         Drag::start_grab (event, cursor);
5272
5273         _editor->get_selection().add (_primary);
5274
5275         MusicFrame where (_primary->region()->position(), 0);
5276         setup_snap_delta (where);
5277
5278         show_verbose_cursor_duration (where.frame, adjusted_current_frame (event), 0);
5279 }
5280
5281 void
5282 TimeFXDrag::motion (GdkEvent* event, bool)
5283 {
5284         RegionView* rv = _primary;
5285         StreamView* cv = rv->get_time_axis_view().view ();
5286         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
5287         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
5288         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
5289         MusicFrame pf (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
5290
5291         _editor->snap_to_with_modifier (pf, event);
5292         pf.frame -= snap_delta (event->button.state);
5293
5294         if (pf.frame > rv->region()->position()) {
5295                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf.frame, layers, layer);
5296         }
5297
5298         show_verbose_cursor_duration (_primary->region()->position(), pf.frame, 0);
5299 }
5300
5301 void
5302 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
5303 {
5304         /* this may have been a single click, no drag. We still want the dialog
5305            to show up in that case, so that the user can manually edit the
5306            parameters for the timestretch.
5307         */
5308
5309         float fraction = 1.0;
5310
5311         if (movement_occurred) {
5312
5313                 motion (event, false);
5314
5315                 _primary->get_time_axis_view().hide_timestretch ();
5316
5317                 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
5318
5319                 if (adjusted_frame_pos < _primary->region()->position()) {
5320                         /* backwards drag of the left edge - not usable */
5321                         return;
5322                 }
5323
5324                 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
5325
5326                 fraction = (double) newlen / (double) _primary->region()->length();
5327
5328 #ifndef USE_RUBBERBAND
5329                 // Soundtouch uses fraction / 100 instead of normal (/ 1)
5330                 if (_primary->region()->data_type() == DataType::AUDIO) {
5331                         fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
5332                 }
5333 #endif
5334         }
5335
5336         if (!_editor->get_selection().regions.empty()) {
5337                 /* primary will already be included in the selection, and edit
5338                    group shared editing will propagate selection across
5339                    equivalent regions, so just use the current region
5340                    selection.
5341                 */
5342
5343                 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
5344                         error << _("An error occurred while executing time stretch operation") << endmsg;
5345                 }
5346         }
5347 }
5348
5349 void
5350 TimeFXDrag::aborted (bool)
5351 {
5352         _primary->get_time_axis_view().hide_timestretch ();
5353 }
5354
5355 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
5356         : Drag (e, i)
5357 {
5358         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
5359 }
5360
5361 void
5362 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5363 {
5364         Drag::start_grab (event);
5365 }
5366
5367 void
5368 ScrubDrag::motion (GdkEvent* /*event*/, bool)
5369 {
5370         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
5371 }
5372
5373 void
5374 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
5375 {
5376         if (movement_occurred && _editor->session()) {
5377                 /* make sure we stop */
5378                 _editor->session()->request_transport_speed (0.0);
5379         }
5380 }
5381
5382 void
5383 ScrubDrag::aborted (bool)
5384 {
5385         /* XXX: TODO */
5386 }
5387
5388 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5389         : Drag (e, i)
5390         , _operation (o)
5391         , _add (false)
5392         , _track_selection_at_start (e)
5393         , _time_selection_at_start (!_editor->get_selection().time.empty())
5394 {
5395         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
5396
5397         if (_time_selection_at_start) {
5398                 start_at_start = _editor->get_selection().time.start();
5399                 end_at_start = _editor->get_selection().time.end_frame();
5400         }
5401 }
5402
5403 void
5404 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
5405 {
5406         if (_editor->session() == 0) {
5407                 return;
5408         }
5409
5410         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5411
5412         switch (_operation) {
5413         case CreateSelection:
5414                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5415                         _add = true;
5416                 } else {
5417                         _add = false;
5418                 }
5419                 cursor = _editor->cursors()->selector;
5420                 Drag::start_grab (event, cursor);
5421                 break;
5422
5423         case SelectionStartTrim:
5424                 if (_editor->clicked_axisview) {
5425                         _editor->clicked_axisview->order_selection_trims (_item, true);
5426                 }
5427                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
5428                 break;
5429
5430         case SelectionEndTrim:
5431                 if (_editor->clicked_axisview) {
5432                         _editor->clicked_axisview->order_selection_trims (_item, false);
5433                 }
5434                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
5435                 break;
5436
5437         case SelectionMove:
5438                 Drag::start_grab (event, cursor);
5439                 break;
5440
5441         case SelectionExtend:
5442                 Drag::start_grab (event, cursor);
5443                 break;
5444         }
5445
5446         if (_operation == SelectionMove) {
5447                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5448         } else {
5449                 show_verbose_cursor_time (adjusted_current_frame (event));
5450         }
5451 }
5452
5453 void
5454 SelectionDrag::setup_pointer_frame_offset ()
5455 {
5456         switch (_operation) {
5457         case CreateSelection:
5458                 _pointer_frame_offset = 0;
5459                 break;
5460
5461         case SelectionStartTrim:
5462         case SelectionMove:
5463                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5464                 break;
5465
5466         case SelectionEndTrim:
5467                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5468                 break;
5469
5470         case SelectionExtend:
5471                 break;
5472         }
5473 }
5474
5475 void
5476 SelectionDrag::motion (GdkEvent* event, bool first_move)
5477 {
5478         framepos_t start = 0;
5479         framepos_t end = 0;
5480         framecnt_t length = 0;
5481         framecnt_t distance = 0;
5482         MusicFrame start_mf (0, 0);
5483         framepos_t const pending_position = adjusted_current_frame (event);
5484
5485         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5486                 return;
5487         }
5488
5489         if (first_move) {
5490                 _track_selection_at_start = _editor->selection->tracks;
5491         }
5492
5493         switch (_operation) {
5494         case CreateSelection:
5495         {
5496                 MusicFrame grab (grab_frame (), 0);
5497                 if (first_move) {
5498                         grab.frame = adjusted_current_frame (event, false);
5499                         if (grab.frame < pending_position) {
5500                                 _editor->snap_to (grab, RoundDownMaybe);
5501                         }  else {
5502                                 _editor->snap_to (grab, RoundUpMaybe);
5503                         }
5504                 }
5505
5506                 if (pending_position < grab.frame) {
5507                         start = pending_position;
5508                         end = grab.frame;
5509                 } else {
5510                         end = pending_position;
5511                         start = grab.frame;
5512                 }
5513
5514                 /* first drag: Either add to the selection
5515                    or create a new selection
5516                 */
5517
5518                 if (first_move) {
5519
5520                         if (_add) {
5521
5522                                 /* adding to the selection */
5523                                 _editor->set_selected_track_as_side_effect (Selection::Add);
5524                                 _editor->clicked_selection = _editor->selection->add (start, end);
5525                                 _add = false;
5526
5527                         } else {
5528
5529                                 /* new selection */
5530
5531                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5532                                         _editor->set_selected_track_as_side_effect (Selection::Set);
5533                                 }
5534
5535                                 _editor->clicked_selection = _editor->selection->set (start, end);
5536                         }
5537                 }
5538
5539                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5540                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5541                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5542                 if (atest) {
5543                         _editor->selection->add (atest);
5544                         break;
5545                 }
5546
5547                 /* select all tracks within the rectangle that we've marked out so far */
5548                 TrackViewList new_selection;
5549                 TrackViewList& all_tracks (_editor->track_views);
5550
5551                 ArdourCanvas::Coord const top = grab_y();
5552                 ArdourCanvas::Coord const bottom = current_pointer_y();
5553
5554                 if (top >= 0 && bottom >= 0) {
5555
5556                         //first, find the tracks that are covered in the y range selection
5557                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5558                                 if ((*i)->covered_by_y_range (top, bottom)) {
5559                                         new_selection.push_back (*i);
5560                                 }
5561                         }
5562
5563                         //now compare our list with the current selection, and add as necessary
5564                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5565                         TrackViewList tracks_to_add;
5566                         TrackViewList tracks_to_remove;
5567                         vector<RouteGroup*> selected_route_groups;
5568
5569                         if (!first_move) {
5570                                 for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i) {
5571                                         if (!new_selection.contains (*i) && !_track_selection_at_start.contains (*i)) {
5572                                                 tracks_to_remove.push_back (*i);
5573                                         } else {
5574                                                 RouteGroup* rg = (*i)->route_group();
5575                                                 if (rg && rg->is_active() && rg->is_select()) {
5576                                                         selected_route_groups.push_back (rg);
5577                                                 }
5578                                         }
5579                                 }
5580                         }
5581
5582                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5583                                 if (!_editor->selection->tracks.contains (*i)) {
5584                                         tracks_to_add.push_back (*i);
5585                                         RouteGroup* rg = (*i)->route_group();
5586
5587                                         if (rg && rg->is_active() && rg->is_select()) {
5588                                                 selected_route_groups.push_back (rg);
5589                                         }
5590                                 }
5591                         }
5592
5593                         _editor->selection->add (tracks_to_add);
5594
5595                         if (!tracks_to_remove.empty()) {
5596
5597                                 /* check all these to-be-removed tracks against the
5598                                  * possibility that they are selected by being
5599                                  * in the same group as an approved track.
5600                                  */
5601
5602                                 for (TrackViewList::iterator i = tracks_to_remove.begin(); i != tracks_to_remove.end(); ) {
5603                                         RouteGroup* rg = (*i)->route_group();
5604
5605                                         if (rg && find (selected_route_groups.begin(), selected_route_groups.end(), rg) != selected_route_groups.end()) {
5606                                                 i = tracks_to_remove.erase (i);
5607                                         } else {
5608                                                 ++i;
5609                                         }
5610                                 }
5611
5612                                 /* remove whatever is left */
5613
5614                                 _editor->selection->remove (tracks_to_remove);
5615                         }
5616                 }
5617         }
5618         break;
5619
5620         case SelectionStartTrim:
5621
5622                 end = _editor->selection->time[_editor->clicked_selection].end;
5623
5624                 if (pending_position > end) {
5625                         start = end;
5626                 } else {
5627                         start = pending_position;
5628                 }
5629                 break;
5630
5631         case SelectionEndTrim:
5632
5633                 start = _editor->selection->time[_editor->clicked_selection].start;
5634
5635                 if (pending_position < start) {
5636                         end = start;
5637                 } else {
5638                         end = pending_position;
5639                 }
5640
5641                 break;
5642
5643         case SelectionMove:
5644
5645                 start = _editor->selection->time[_editor->clicked_selection].start;
5646                 end = _editor->selection->time[_editor->clicked_selection].end;
5647
5648                 length = end - start;
5649                 distance = pending_position - start;
5650                 start = pending_position;
5651
5652                 start_mf.frame = start;
5653                 _editor->snap_to (start_mf);
5654
5655                 end = start_mf.frame + length;
5656
5657                 break;
5658
5659         case SelectionExtend:
5660                 break;
5661         }
5662
5663         if (start != end) {
5664                 switch (_operation) {
5665                 case SelectionMove:
5666                         if (_time_selection_at_start) {
5667                                 _editor->selection->move_time (distance);
5668                         }
5669                         break;
5670                 default:
5671                         _editor->selection->replace (_editor->clicked_selection, start, end);
5672                 }
5673         }
5674
5675         if (_operation == SelectionMove) {
5676                 show_verbose_cursor_time(start);
5677         } else {
5678                 show_verbose_cursor_time(pending_position);
5679         }
5680 }
5681
5682 void
5683 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5684 {
5685         Session* s = _editor->session();
5686
5687         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5688         if (movement_occurred) {
5689                 motion (event, false);
5690                 /* XXX this is not object-oriented programming at all. ick */
5691                 if (_editor->selection->time.consolidate()) {
5692                         _editor->selection->TimeChanged ();
5693                 }
5694
5695                 /* XXX what if its a music time selection? */
5696                 if (s) {
5697                         if (s->get_play_range() && s->transport_rolling()) {
5698                                 s->request_play_range (&_editor->selection->time, true);
5699                         } else if (!s->config.get_external_sync()) {
5700                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5701                                         s->request_locate (_editor->get_selection().time.start());
5702                                 }
5703                         }
5704
5705                         if (_editor->get_selection().time.length() != 0) {
5706                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5707                         } else {
5708                                 s->clear_range_selection ();
5709                         }
5710                 }
5711
5712         } else {
5713                 /* just a click, no pointer movement.
5714                  */
5715
5716                 if (was_double_click()) {
5717                         if (UIConfiguration::instance().get_use_double_click_to_zoom_to_selection()) {
5718                                 _editor->temporal_zoom_selection (Both);
5719                                 return;
5720                         }
5721                 }
5722
5723                 if (_operation == SelectionExtend) {
5724                         if (_time_selection_at_start) {
5725                                 framepos_t pos = adjusted_current_frame (event, false);
5726                                 framepos_t start = min (pos, start_at_start);
5727                                 framepos_t end = max (pos, end_at_start);
5728                                 _editor->selection->set (start, end);
5729                         }
5730                 } else {
5731                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5732                                 if (_editor->clicked_selection) {
5733                                         _editor->selection->remove (_editor->clicked_selection);
5734                                 }
5735                         } else {
5736                                 if (!_editor->clicked_selection) {
5737                                         _editor->selection->clear_time();
5738                                 }
5739                         }
5740                 }
5741
5742                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5743                         _editor->selection->set (_editor->clicked_axisview);
5744                 }
5745
5746                 if (s && s->get_play_range () && s->transport_rolling()) {
5747                         s->request_stop (false, false);
5748                 }
5749
5750         }
5751
5752         _editor->stop_canvas_autoscroll ();
5753         _editor->clicked_selection = 0;
5754         _editor->commit_reversible_selection_op ();
5755 }
5756
5757 void
5758 SelectionDrag::aborted (bool)
5759 {
5760         /* XXX: TODO */
5761 }
5762
5763 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5764         : Drag (e, i, false),
5765           _operation (o),
5766           _copy (false)
5767 {
5768         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5769
5770         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5771                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5772                                                                       physical_screen_height (_editor->current_toplevel()->get_window())));
5773         _drag_rect->hide ();
5774
5775         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5776         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5777 }
5778
5779 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5780 {
5781         /* normal canvas items will be cleaned up when their parent group is deleted. But
5782            this item is created as the child of a long-lived parent group, and so we
5783            need to explicitly delete it.
5784         */
5785         delete _drag_rect;
5786 }
5787
5788 void
5789 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5790 {
5791         if (_editor->session() == 0) {
5792                 return;
5793         }
5794
5795         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5796
5797         if (!_editor->temp_location) {
5798                 _editor->temp_location = new Location (*_editor->session());
5799         }
5800
5801         switch (_operation) {
5802         case CreateSkipMarker:
5803         case CreateRangeMarker:
5804         case CreateTransportMarker:
5805         case CreateCDMarker:
5806
5807                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5808                         _copy = true;
5809                 } else {
5810                         _copy = false;
5811                 }
5812                 cursor = _editor->cursors()->selector;
5813                 break;
5814         }
5815
5816         Drag::start_grab (event, cursor);
5817
5818         show_verbose_cursor_time (adjusted_current_frame (event));
5819 }
5820
5821 void
5822 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5823 {
5824         framepos_t start = 0;
5825         framepos_t end = 0;
5826         ArdourCanvas::Rectangle *crect;
5827
5828         switch (_operation) {
5829         case CreateSkipMarker:
5830                 crect = _editor->range_bar_drag_rect;
5831                 break;
5832         case CreateRangeMarker:
5833                 crect = _editor->range_bar_drag_rect;
5834                 break;
5835         case CreateTransportMarker:
5836                 crect = _editor->transport_bar_drag_rect;
5837                 break;
5838         case CreateCDMarker:
5839                 crect = _editor->cd_marker_bar_drag_rect;
5840                 break;
5841         default:
5842                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5843                 return;
5844                 break;
5845         }
5846
5847         framepos_t const pf = adjusted_current_frame (event);
5848
5849         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5850                 MusicFrame grab (grab_frame (), 0);
5851                 _editor->snap_to (grab);
5852
5853                 if (pf < grab_frame()) {
5854                         start = pf;
5855                         end = grab.frame;
5856                 } else {
5857                         end = pf;
5858                         start = grab.frame;
5859                 }
5860
5861                 /* first drag: Either add to the selection
5862                    or create a new selection.
5863                 */
5864
5865                 if (first_move) {
5866
5867                         _editor->temp_location->set (start, end);
5868
5869                         crect->show ();
5870
5871                         update_item (_editor->temp_location);
5872                         _drag_rect->show();
5873                         //_drag_rect->raise_to_top();
5874
5875                 }
5876         }
5877
5878         if (start != end) {
5879                 _editor->temp_location->set (start, end);
5880
5881                 double x1 = _editor->sample_to_pixel (start);
5882                 double x2 = _editor->sample_to_pixel (end);
5883                 crect->set_x0 (x1);
5884                 crect->set_x1 (x2);
5885
5886                 update_item (_editor->temp_location);
5887         }
5888
5889         show_verbose_cursor_time (pf);
5890
5891 }
5892
5893 void
5894 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5895 {
5896         Location * newloc = 0;
5897         string rangename;
5898         int flags;
5899
5900         if (movement_occurred) {
5901                 motion (event, false);
5902                 _drag_rect->hide();
5903
5904                 switch (_operation) {
5905                 case CreateSkipMarker:
5906                 case CreateRangeMarker:
5907                 case CreateCDMarker:
5908                 {
5909                         XMLNode &before = _editor->session()->locations()->get_state();
5910                         if (_operation == CreateSkipMarker) {
5911                                 _editor->begin_reversible_command (_("new skip marker"));
5912                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5913                                 flags = Location::IsRangeMarker | Location::IsSkip;
5914                                 _editor->range_bar_drag_rect->hide();
5915                         } else if (_operation == CreateCDMarker) {
5916                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5917                                 _editor->begin_reversible_command (_("new CD marker"));
5918                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5919                                 _editor->cd_marker_bar_drag_rect->hide();
5920                         } else {
5921                                 _editor->begin_reversible_command (_("new skip marker"));
5922                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5923                                 flags = Location::IsRangeMarker;
5924                                 _editor->range_bar_drag_rect->hide();
5925                         }
5926                         newloc = new Location (
5927                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5928                                 , _editor->get_grid_music_divisions (event->button.state));
5929
5930                         _editor->session()->locations()->add (newloc, true);
5931                         XMLNode &after = _editor->session()->locations()->get_state();
5932                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5933                         _editor->commit_reversible_command ();
5934                         break;
5935                 }
5936
5937                 case CreateTransportMarker:
5938                         // popup menu to pick loop or punch
5939                         _editor->new_transport_marker_context_menu (&event->button, _item);
5940                         break;
5941                 }
5942
5943         } else {
5944
5945                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5946
5947                 if (_operation == CreateTransportMarker) {
5948
5949                         /* didn't drag, so just locate */
5950
5951                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5952
5953                 } else if (_operation == CreateCDMarker) {
5954
5955                         /* didn't drag, but mark is already created so do
5956                          * nothing */
5957
5958                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5959
5960                         framepos_t start;
5961                         framepos_t end;
5962
5963                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5964
5965                         if (end == max_framepos) {
5966                                 end = _editor->session()->current_end_frame ();
5967                         }
5968
5969                         if (start == max_framepos) {
5970                                 start = _editor->session()->current_start_frame ();
5971                         }
5972
5973                         switch (_editor->mouse_mode) {
5974                         case MouseObject:
5975                                 /* find the two markers on either side and then make the selection from it */
5976                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5977                                 break;
5978
5979                         case MouseRange:
5980                                 /* find the two markers on either side of the click and make the range out of it */
5981                                 _editor->selection->set (start, end);
5982                                 break;
5983
5984                         default:
5985                                 break;
5986                         }
5987                 }
5988         }
5989
5990         _editor->stop_canvas_autoscroll ();
5991 }
5992
5993 void
5994 RangeMarkerBarDrag::aborted (bool movement_occurred)
5995 {
5996         if (movement_occurred) {
5997                 _drag_rect->hide ();
5998         }
5999 }
6000
6001 void
6002 RangeMarkerBarDrag::update_item (Location* location)
6003 {
6004         double const x1 = _editor->sample_to_pixel (location->start());
6005         double const x2 = _editor->sample_to_pixel (location->end());
6006
6007         _drag_rect->set_x0 (x1);
6008         _drag_rect->set_x1 (x2);
6009 }
6010
6011 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
6012         : Drag (e, i)
6013         , _cumulative_dx (0)
6014         , _cumulative_dy (0)
6015         , _earliest (0.0)
6016         , _was_selected (false)
6017         , _copy (false)
6018 {
6019         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
6020
6021         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
6022         assert (_primary);
6023         _region = &_primary->region_view ();
6024         _note_height = _region->midi_stream_view()->note_height ();
6025 }
6026
6027 void
6028 NoteDrag::setup_pointer_frame_offset ()
6029 {
6030         _pointer_frame_offset = raw_grab_frame()
6031                 - _editor->session()->tempo_map().frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
6032 }
6033
6034 void
6035 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
6036 {
6037         Drag::start_grab (event);
6038
6039         if (ArdourKeyboard::indicates_copy (event->button.state)) {
6040                 _copy = true;
6041         } else {
6042                 _copy = false;
6043         }
6044
6045         setup_snap_delta (MusicFrame (_region->source_beats_to_absolute_frames (_primary->note()->time ()), 0));
6046
6047         if (!(_was_selected = _primary->selected())) {
6048
6049                 /* tertiary-click means extend selection - we'll do that on button release,
6050                    so don't add it here, because otherwise we make it hard to figure
6051                    out the "extend-to" range.
6052                 */
6053
6054                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
6055
6056                 if (!extend) {
6057                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
6058
6059                         if (add) {
6060                                 _region->note_selected (_primary, true);
6061                         } else {
6062                                 _editor->get_selection().clear_points();
6063                                 _region->unique_select (_primary);
6064                         }
6065                 }
6066         }
6067 }
6068
6069 /** @return Current total drag x change in quarter notes */
6070 double
6071 NoteDrag::total_dx (GdkEvent * event) const
6072 {
6073         if (_x_constrained) {
6074                 return 0;
6075         }
6076
6077         TempoMap& map (_editor->session()->tempo_map());
6078
6079         /* dx in frames */
6080         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
6081
6082         /* primary note time */
6083         frameoffset_t const n = map.frame_at_quarter_note (_region->session_relative_qn (_primary->note()->time().to_double()));
6084
6085         /* primary note time in quarter notes */
6086         double const n_qn = _region->session_relative_qn (_primary->note()->time().to_double());
6087
6088         /* new time of the primary note in session frames */
6089         frameoffset_t st = n + dx + snap_delta (event->button.state);
6090
6091         /* possibly snap and return corresponding delta in quarter notes */
6092         MusicFrame snap (st, 0);
6093         _editor->snap_to_with_modifier (snap, event);
6094         double ret = map.exact_qn_at_frame (snap.frame, snap.division) - n_qn - snap_delta_music (event->button.state);
6095
6096         /* prevent the earliest note being dragged earlier than the region's start position */
6097         if (_earliest + ret < _region->midi_region()->start_beats()) {
6098                 ret -= (_earliest + ret) - _region->midi_region()->start_beats();
6099         }
6100
6101         return ret;
6102 }
6103
6104 /** @return Current total drag y change in note number */
6105 int8_t
6106 NoteDrag::total_dy () const
6107 {
6108         if (_y_constrained) {
6109                 return 0;
6110         }
6111
6112         double const y = _region->midi_view()->y_position ();
6113         /* new current note */
6114         uint8_t n = _region->y_to_note (current_pointer_y () - y);
6115         /* clamp */
6116         MidiStreamView* msv = _region->midi_stream_view ();
6117         n = max (msv->lowest_note(), n);
6118         n = min (msv->highest_note(), n);
6119         /* and work out delta */
6120         return n - _region->y_to_note (grab_y() - y);
6121 }
6122
6123 void
6124 NoteDrag::motion (GdkEvent * event, bool first_move)
6125 {
6126         if (first_move) {
6127                 _earliest = _region->earliest_in_selection().to_double();
6128                 if (_copy) {
6129                         /* make copies of all the selected notes */
6130                         _primary = _region->copy_selection (_primary);
6131                 }
6132         }
6133
6134         /* Total change in x and y since the start of the drag */
6135         double const dx_qn = total_dx (event);
6136         int8_t const dy = total_dy ();
6137
6138         /* Now work out what we have to do to the note canvas items to set this new drag delta */
6139         double const tdx = _x_constrained ? 0 : dx_qn - _cumulative_dx;
6140         double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
6141
6142         if (tdx || tdy) {
6143                 _cumulative_dx = dx_qn;
6144                 _cumulative_dy += tdy;
6145
6146                 int8_t note_delta = total_dy();
6147
6148                 if (tdx || tdy) {
6149                         if (_copy) {
6150                                 _region->move_copies (dx_qn, tdy, note_delta);
6151                         } else {
6152                                 _region->move_selection (dx_qn, tdy, note_delta);
6153                         }
6154
6155                         /* the new note value may be the same as the old one, but we
6156                          * don't know what that means because the selection may have
6157                          * involved more than one note and we might be doing something
6158                          * odd with them. so show the note value anyway, always.
6159                          */
6160
6161                         uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
6162
6163                         _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
6164                 }
6165         }
6166 }
6167
6168 void
6169 NoteDrag::finished (GdkEvent* ev, bool moved)
6170 {
6171         if (!moved) {
6172                 /* no motion - select note */
6173
6174                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
6175                     _editor->current_mouse_mode() == Editing::MouseDraw) {
6176
6177                         bool changed = false;
6178
6179                         if (_was_selected) {
6180                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6181                                 if (add) {
6182                                         _region->note_deselected (_primary);
6183                                         changed = true;
6184                                 } else {
6185                                         _editor->get_selection().clear_points();
6186                                         _region->unique_select (_primary);
6187                                         changed = true;
6188                                 }
6189                         } else {
6190                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
6191                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
6192
6193                                 if (!extend && !add && _region->selection_size() > 1) {
6194                                         _editor->get_selection().clear_points();
6195                                         _region->unique_select (_primary);
6196                                         changed = true;
6197                                 } else if (extend) {
6198                                         _region->note_selected (_primary, true, true);
6199                                         changed = true;
6200                                 } else {
6201                                         /* it was added during button press */
6202                                         changed = true;
6203
6204                                 }
6205                         }
6206
6207                         if (changed) {
6208                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
6209                                 _editor->commit_reversible_selection_op();
6210                         }
6211                 }
6212         } else {
6213                 _region->note_dropped (_primary, total_dx (ev), total_dy(), _copy);
6214         }
6215 }
6216
6217 void
6218 NoteDrag::aborted (bool)
6219 {
6220         /* XXX: TODO */
6221 }
6222
6223 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
6224 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
6225         : Drag (editor, atv->base_item ())
6226         , _ranges (r)
6227         , _y_origin (atv->y_position())
6228         , _nothing_to_drag (false)
6229 {
6230         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6231         setup (atv->lines ());
6232 }
6233
6234 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
6235 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
6236         : Drag (editor, rv->get_canvas_group ())
6237         , _ranges (r)
6238         , _y_origin (rv->get_time_axis_view().y_position())
6239         , _nothing_to_drag (false)
6240         , _integral (false)
6241 {
6242         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
6243
6244         list<boost::shared_ptr<AutomationLine> > lines;
6245
6246         AudioRegionView*      audio_view;
6247         AutomationRegionView* automation_view;
6248         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
6249                 lines.push_back (audio_view->get_gain_line ());
6250         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
6251                 lines.push_back (automation_view->line ());
6252                 _integral = true;
6253         } else {
6254                 error << _("Automation range drag created for invalid region type") << endmsg;
6255         }
6256
6257         setup (lines);
6258 }
6259
6260 /** @param lines AutomationLines to drag.
6261  *  @param offset Offset from the session start to the points in the AutomationLines.
6262  */
6263 void
6264 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
6265 {
6266         /* find the lines that overlap the ranges being dragged */
6267         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
6268         while (i != lines.end ()) {
6269                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
6270                 ++j;
6271
6272                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
6273
6274                 /* check this range against all the AudioRanges that we are using */
6275                 list<AudioRange>::const_iterator k = _ranges.begin ();
6276                 while (k != _ranges.end()) {
6277                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
6278                                 break;
6279                         }
6280                         ++k;
6281                 }
6282
6283                 /* add it to our list if it overlaps at all */
6284                 if (k != _ranges.end()) {
6285                         Line n;
6286                         n.line = *i;
6287                         n.state = 0;
6288                         n.range = r;
6289                         _lines.push_back (n);
6290                 }
6291
6292                 i = j;
6293         }
6294
6295         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
6296 }
6297
6298 double
6299 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
6300 {
6301         return 1.0 - ((global_y - _y_origin) / line->height());
6302 }
6303
6304 double
6305 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
6306 {
6307         const double v = list->eval(x);
6308         return _integral ? rint(v) : v;
6309 }
6310
6311 void
6312 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6313 {
6314         Drag::start_grab (event, cursor);
6315
6316         /* Get line states before we start changing things */
6317         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6318                 i->state = &i->line->get_state ();
6319                 i->original_fraction = y_fraction (i->line, current_pointer_y());
6320         }
6321
6322         if (_ranges.empty()) {
6323
6324                 /* No selected time ranges: drag all points */
6325                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6326                         uint32_t const N = i->line->npoints ();
6327                         for (uint32_t j = 0; j < N; ++j) {
6328                                 i->points.push_back (i->line->nth (j));
6329                         }
6330                 }
6331
6332         }
6333
6334         if (_nothing_to_drag) {
6335                 return;
6336         }
6337 }
6338
6339 void
6340 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
6341 {
6342         if (_nothing_to_drag && !first_move) {
6343                 return;
6344         }
6345
6346         if (first_move) {
6347                 _editor->begin_reversible_command (_("automation range move"));
6348
6349                 if (!_ranges.empty()) {
6350
6351                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
6352
6353                                 framecnt_t const half = (i->start + i->end) / 2;
6354
6355                                 /* find the line that this audio range starts in */
6356                                 list<Line>::iterator j = _lines.begin();
6357                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
6358                                         ++j;
6359                                 }
6360
6361                                 if (j != _lines.end()) {
6362                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6363
6364                                 /* j is the line that this audio range starts in; fade into it;
6365                                    64 samples length plucked out of thin air.
6366                                 */
6367
6368                                         framepos_t a = i->start + 64;
6369                                         if (a > half) {
6370                                                 a = half;
6371                                         }
6372
6373                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
6374                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
6375
6376                                         XMLNode &before = the_list->get_state();
6377                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6378                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6379
6380                                         if (add_p || add_q) {
6381                                                 _editor->session()->add_command (
6382                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6383                                         }
6384                                 }
6385
6386                                 /* same thing for the end */
6387
6388                                 j = _lines.begin();
6389                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
6390                                         ++j;
6391                                 }
6392
6393                                 if (j != _lines.end()) {
6394                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
6395
6396                                         /* j is the line that this audio range starts in; fade out of it;
6397                                            64 samples length plucked out of thin air.
6398                                         */
6399
6400                                         framepos_t b = i->end - 64;
6401                                         if (b < half) {
6402                                                 b = half;
6403                                         }
6404
6405                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
6406                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
6407
6408                                         XMLNode &before = the_list->get_state();
6409                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
6410                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
6411
6412                                         if (add_p || add_q) {
6413                                                 _editor->session()->add_command (
6414                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
6415                                         }
6416                                 }
6417                         }
6418
6419                         _nothing_to_drag = true;
6420
6421                         /* Find all the points that should be dragged and put them in the relevant
6422                            points lists in the Line structs.
6423                         */
6424
6425                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6426
6427                                 uint32_t const N = i->line->npoints ();
6428                                 for (uint32_t j = 0; j < N; ++j) {
6429
6430                                         /* here's a control point on this line */
6431                                         ControlPoint* p = i->line->nth (j);
6432                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
6433
6434                                         /* see if it's inside a range */
6435                                         list<AudioRange>::const_iterator k = _ranges.begin ();
6436                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
6437                                                 ++k;
6438                                         }
6439
6440                                         if (k != _ranges.end()) {
6441                                                 /* dragging this point */
6442                                                 _nothing_to_drag = false;
6443                                                 i->points.push_back (p);
6444                                         }
6445                                 }
6446                         }
6447                 }
6448
6449                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6450                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
6451                 }
6452         }
6453
6454         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
6455                 float const f = y_fraction (l->line, current_pointer_y());
6456                 /* we are ignoring x position for this drag, so we can just pass in anything */
6457                 pair<float, float> result;
6458                 uint32_t ignored;
6459                 result = l->line->drag_motion (0, f, true, false, ignored);
6460                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (result.first, result.second));
6461         }
6462 }
6463
6464 void
6465 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
6466 {
6467         if (_nothing_to_drag || !motion_occurred) {
6468                 return;
6469         }
6470
6471         motion (event, false);
6472         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6473                 i->line->end_drag (false, 0);
6474         }
6475
6476         _editor->commit_reversible_command ();
6477 }
6478
6479 void
6480 AutomationRangeDrag::aborted (bool)
6481 {
6482         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
6483                 i->line->reset ();
6484         }
6485 }
6486
6487 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
6488         : view (v)
6489         , initial_time_axis_view (itav)
6490 {
6491         /* note that time_axis_view may be null if the regionview was created
6492          * as part of a copy operation.
6493          */
6494         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6495         layer = v->region()->layer ();
6496         initial_y = v->get_canvas_group()->position().y;
6497         initial_playlist = v->region()->playlist ();
6498         initial_position = v->region()->position ();
6499         initial_end = v->region()->position () + v->region()->length ();
6500 }
6501
6502 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6503         : Drag (e, i->canvas_item ())
6504         , _region_view (r)
6505         , _patch_change (i)
6506         , _cumulative_dx (0)
6507 {
6508         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6509                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6510                                                    grab_frame()));
6511 }
6512
6513 void
6514 PatchChangeDrag::motion (GdkEvent* ev, bool)
6515 {
6516         framepos_t f = adjusted_current_frame (ev);
6517         boost::shared_ptr<Region> r = _region_view->region ();
6518         f = max (f, r->position ());
6519         f = min (f, r->last_frame ());
6520
6521         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6522         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6523         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6524         _cumulative_dx = dxu;
6525 }
6526
6527 void
6528 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6529 {
6530         if (!movement_occurred) {
6531                 if (was_double_click()) {
6532                         _region_view->edit_patch_change (_patch_change);
6533                 }
6534                 return;
6535         }
6536
6537         boost::shared_ptr<Region> r (_region_view->region ());
6538         framepos_t f = adjusted_current_frame (ev);
6539         f = max (f, r->position ());
6540         f = min (f, r->last_frame ());
6541
6542         _region_view->move_patch_change (
6543                 *_patch_change,
6544                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6545                 );
6546 }
6547
6548 void
6549 PatchChangeDrag::aborted (bool)
6550 {
6551         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6552 }
6553
6554 void
6555 PatchChangeDrag::setup_pointer_frame_offset ()
6556 {
6557         boost::shared_ptr<Region> region = _region_view->region ();
6558         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6559 }
6560
6561 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6562         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6563         , _region_view (rv)
6564 {
6565
6566 }
6567
6568 void
6569 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6570 {
6571         _region_view->update_drag_selection (
6572                 x1, x2, y1, y2,
6573                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6574 }
6575
6576 void
6577 MidiRubberbandSelectDrag::deselect_things ()
6578 {
6579         /* XXX */
6580 }
6581
6582 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6583         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6584         , _region_view (rv)
6585 {
6586         _vertical_only = true;
6587 }
6588
6589 void
6590 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6591 {
6592         double const y = _region_view->midi_view()->y_position ();
6593
6594         y1 = max (0.0, y1 - y);
6595         y2 = max (0.0, y2 - y);
6596
6597         _region_view->update_vertical_drag_selection (
6598                 y1,
6599                 y2,
6600                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6601                 );
6602 }
6603
6604 void
6605 MidiVerticalSelectDrag::deselect_things ()
6606 {
6607         /* XXX */
6608 }
6609
6610 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6611         : RubberbandSelectDrag (e, i)
6612 {
6613
6614 }
6615
6616 void
6617 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6618 {
6619         if (drag_in_progress) {
6620                 /* We just want to select things at the end of the drag, not during it */
6621                 return;
6622         }
6623
6624         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6625
6626         _editor->begin_reversible_selection_op (X_("rubberband selection"));
6627
6628         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6629
6630         _editor->commit_reversible_selection_op ();
6631 }
6632
6633 void
6634 EditorRubberbandSelectDrag::deselect_things ()
6635 {
6636         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6637
6638         _editor->selection->clear_tracks();
6639         _editor->selection->clear_regions();
6640         _editor->selection->clear_points ();
6641         _editor->selection->clear_lines ();
6642         _editor->selection->clear_midi_notes ();
6643
6644         _editor->commit_reversible_selection_op();
6645 }
6646
6647 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6648         : Drag (e, i)
6649         , _region_view (rv)
6650         , _drag_rect (0)
6651 {
6652         _note[0] = _note[1] = 0;
6653 }
6654
6655 NoteCreateDrag::~NoteCreateDrag ()
6656 {
6657         delete _drag_rect;
6658 }
6659
6660 framecnt_t
6661 NoteCreateDrag::grid_frames (framepos_t t) const
6662 {
6663
6664         const Evoral::Beats grid_beats = _region_view->get_grid_beats (t);
6665         const Evoral::Beats t_beats = _region_view->region_frames_to_region_beats (t);
6666
6667         return _region_view->region_beats_to_region_frames (t_beats + grid_beats)
6668                 - _region_view->region_beats_to_region_frames (t_beats);
6669 }
6670
6671 void
6672 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6673 {
6674         Drag::start_grab (event, cursor);
6675
6676         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6677         TempoMap& map (_editor->session()->tempo_map());
6678
6679         const framepos_t pf = _drags->current_pointer_frame ();
6680         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6681
6682         const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6683
6684         double eqaf = map.exact_qn_at_frame (pf, divisions);
6685
6686         if (divisions != 0) {
6687
6688                 const double qaf = map.quarter_note_at_frame (pf);
6689
6690                 /* Hack so that we always snap to the note that we are over, instead of snapping
6691                    to the next one if we're more than halfway through the one we're over.
6692                 */
6693
6694                 const double rem = eqaf - qaf;
6695                 if (rem >= 0.0) {
6696                         eqaf -= grid_beats.to_double();
6697                 }
6698         }
6699
6700         _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6701         /* minimum initial length is grid beats */
6702         _note[1] = map.frame_at_quarter_note (eqaf + grid_beats.to_double()) - _region_view->region()->position();
6703
6704         double const x0 = _editor->sample_to_pixel (_note[0]);
6705         double const x1 = _editor->sample_to_pixel (_note[1]);
6706         double const y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6707
6708         _drag_rect->set (ArdourCanvas::Rect (x0, y, x1, y + floor (_region_view->midi_stream_view()->note_height ())));
6709         _drag_rect->set_outline_all ();
6710         _drag_rect->set_outline_color (0xffffff99);
6711         _drag_rect->set_fill_color (0xffffff66);
6712 }
6713
6714 void
6715 NoteCreateDrag::motion (GdkEvent* event, bool)
6716 {
6717         TempoMap& map (_editor->session()->tempo_map());
6718         const framepos_t pf = _drags->current_pointer_frame ();
6719         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6720         double eqaf = map.exact_qn_at_frame (pf, divisions);
6721
6722         if (divisions != 0) {
6723
6724                 const Evoral::Beats grid_beats = _region_view->get_grid_beats (pf);
6725
6726                 const double qaf = map.quarter_note_at_frame (pf);
6727                 /* Hack so that we always snap to the note that we are over, instead of snapping
6728                    to the next one if we're more than halfway through the one we're over.
6729                 */
6730
6731                 const double rem = eqaf - qaf;
6732                 if (rem >= 0.0) {
6733                         eqaf -= grid_beats.to_double();
6734                 }
6735
6736                 eqaf += grid_beats.to_double();
6737         }
6738         _note[1] = max ((framepos_t)0, map.frame_at_quarter_note (eqaf) - _region_view->region()->position ());
6739
6740         double const x0 = _editor->sample_to_pixel (_note[0]);
6741         double const x1 = _editor->sample_to_pixel (_note[1]);
6742         _drag_rect->set_x0 (std::min(x0, x1));
6743         _drag_rect->set_x1 (std::max(x0, x1));
6744 }
6745
6746 void
6747 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6748 {
6749         /* we create a note even if there was no movement */
6750         framepos_t const start = min (_note[0], _note[1]);
6751         framepos_t const start_sess_rel = start + _region_view->region()->position();
6752         framecnt_t length = max (_editor->pixel_to_sample (1.0), (framecnt_t) fabs ((double)(_note[0] - _note[1])));
6753         framecnt_t const g = grid_frames (start_sess_rel);
6754
6755         if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6756                 length = g;
6757         }
6758
6759         TempoMap& map (_editor->session()->tempo_map());
6760         const double qn_length = map.quarter_notes_between_frames (start_sess_rel, start_sess_rel + length);
6761         Evoral::Beats qn_length_beats = max (Evoral::Beats::ticks(1), Evoral::Beats (qn_length));
6762
6763         _editor->begin_reversible_command (_("Create Note"));
6764         _region_view->clear_editor_note_selection();
6765         _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6766         _editor->commit_reversible_command ();
6767 }
6768
6769 double
6770 NoteCreateDrag::y_to_region (double y) const
6771 {
6772         double x = 0;
6773         _region_view->get_canvas_group()->canvas_to_item (x, y);
6774         return y;
6775 }
6776
6777 void
6778 NoteCreateDrag::aborted (bool)
6779 {
6780
6781 }
6782
6783 HitCreateDrag::HitCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6784         : Drag (e, i)
6785         , _region_view (rv)
6786         , _last_pos (0)
6787         , _y (0.0)
6788 {
6789 }
6790
6791 HitCreateDrag::~HitCreateDrag ()
6792 {
6793 }
6794
6795 void
6796 HitCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6797 {
6798         Drag::start_grab (event, cursor);
6799
6800         TempoMap& map (_editor->session()->tempo_map());
6801
6802         _y = _region_view->note_to_y (_region_view->y_to_note (y_to_region (event->button.y)));
6803
6804         const framepos_t pf = _drags->current_pointer_frame ();
6805         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6806
6807         const double eqaf = map.exact_qn_at_frame (pf, divisions);
6808
6809         boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6810
6811         if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6812                 return;
6813         }
6814
6815         const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6816         Evoral::Beats length = _region_view->get_grid_beats (pf);
6817
6818         _editor->begin_reversible_command (_("Create Hit"));
6819         _region_view->clear_editor_note_selection();
6820         _region_view->create_note_at (start, _y, length, event->button.state, false);
6821
6822         _last_pos = start;
6823 }
6824
6825 void
6826 HitCreateDrag::motion (GdkEvent* event, bool)
6827 {
6828         TempoMap& map (_editor->session()->tempo_map());
6829
6830         const framepos_t pf = _drags->current_pointer_frame ();
6831         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6832
6833         if (divisions == 0) {
6834                 return;
6835         }
6836
6837         const double eqaf = map.exact_qn_at_frame (pf, divisions);
6838         const framepos_t start = map.frame_at_quarter_note (eqaf) - _region_view->region()->position ();
6839
6840         if (_last_pos == start) {
6841                 return;
6842         }
6843
6844         Evoral::Beats length = _region_view->get_grid_beats (pf);
6845
6846         boost::shared_ptr<MidiRegion> mr = _region_view->midi_region();
6847
6848         if (eqaf >= mr->quarter_note() + mr->length_beats()) {
6849                 return;
6850         }
6851
6852         _region_view->create_note_at (start, _y, length, event->button.state, false);
6853
6854         _last_pos = start;
6855 }
6856
6857 void
6858 HitCreateDrag::finished (GdkEvent* /* ev */, bool /* had_movement */)
6859 {
6860         _editor->commit_reversible_command ();
6861
6862 }
6863
6864 double
6865 HitCreateDrag::y_to_region (double y) const
6866 {
6867         double x = 0;
6868         _region_view->get_canvas_group()->canvas_to_item (x, y);
6869         return y;
6870 }
6871
6872 void
6873 HitCreateDrag::aborted (bool)
6874 {
6875         // umm..
6876 }
6877
6878 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6879         : Drag (e, i)
6880         , arv (rv)
6881         , start (start_yn)
6882 {
6883         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
6884 }
6885
6886 void
6887 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6888 {
6889         Drag::start_grab (event, cursor);
6890 }
6891
6892 void
6893 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6894 {
6895         double distance;
6896         double new_length;
6897         framecnt_t len;
6898
6899         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6900
6901         if (start) {
6902                 distance = _drags->current_pointer_x() - grab_x();
6903                 len = ar->fade_in()->back()->when;
6904         } else {
6905                 distance = grab_x() - _drags->current_pointer_x();
6906                 len = ar->fade_out()->back()->when;
6907         }
6908
6909         /* how long should it be ? */
6910
6911         new_length = len + _editor->pixel_to_sample (distance);
6912
6913         /* now check with the region that this is legal */
6914
6915         new_length = ar->verify_xfade_bounds (new_length, start);
6916
6917         if (start) {
6918                 arv->reset_fade_in_shape_width (ar, new_length);
6919         } else {
6920                 arv->reset_fade_out_shape_width (ar, new_length);
6921         }
6922 }
6923
6924 void
6925 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6926 {
6927         double distance;
6928         double new_length;
6929         framecnt_t len;
6930
6931         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6932
6933         if (start) {
6934                 distance = _drags->current_pointer_x() - grab_x();
6935                 len = ar->fade_in()->back()->when;
6936         } else {
6937                 distance = grab_x() - _drags->current_pointer_x();
6938                 len = ar->fade_out()->back()->when;
6939         }
6940
6941         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6942
6943         _editor->begin_reversible_command ("xfade trim");
6944         ar->playlist()->clear_owned_changes ();
6945
6946         if (start) {
6947                 ar->set_fade_in_length (new_length);
6948         } else {
6949                 ar->set_fade_out_length (new_length);
6950         }
6951
6952         /* Adjusting the xfade may affect other regions in the playlist, so we need
6953            to get undo Commands from the whole playlist rather than just the
6954            region.
6955         */
6956
6957         vector<Command*> cmds;
6958         ar->playlist()->rdiff (cmds);
6959         _editor->session()->add_commands (cmds);
6960         _editor->commit_reversible_command ();
6961
6962 }
6963
6964 void
6965 CrossfadeEdgeDrag::aborted (bool)
6966 {
6967         if (start) {
6968                 // arv->redraw_start_xfade ();
6969         } else {
6970                 // arv->redraw_end_xfade ();
6971         }
6972 }
6973
6974 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6975         : Drag (e, item, true)
6976         , line (new EditorCursor (*e))
6977 {
6978         line->set_position (pos);
6979         line->show ();
6980         line->track_canvas_item().reparent (_editor->_drag_motion_group);
6981 }
6982
6983 RegionCutDrag::~RegionCutDrag ()
6984 {
6985         delete line;
6986 }
6987
6988 void
6989 RegionCutDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
6990 {
6991         Drag::start_grab (event, c);
6992         motion (event, false);
6993 }
6994
6995 void
6996 RegionCutDrag::motion (GdkEvent* event, bool)
6997 {
6998         MusicFrame pos (_drags->current_pointer_frame(), 0);
6999         _editor->snap_to_with_modifier (pos, event);
7000
7001         line->set_position (pos.frame);
7002 }
7003
7004 void
7005 RegionCutDrag::finished (GdkEvent* event, bool)
7006 {
7007         _editor->get_track_canvas()->canvas()->re_enter();
7008
7009
7010         MusicFrame pos (_drags->current_pointer_frame(), 0);
7011         _editor->snap_to_with_modifier (pos, event);
7012         line->hide ();
7013
7014         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos.frame);
7015
7016         if (rs.empty()) {
7017                 return;
7018         }
7019
7020         _editor->split_regions_at (pos, rs, false);
7021 }
7022
7023 void
7024 RegionCutDrag::aborted (bool)
7025 {
7026 }