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