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