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