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