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