make inclusion/exclusion of hidden children optional in Item::add_child_bounding_boxes
[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         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1533
1534                 RegionView* rv = i->view;
1535                 RouteTimeAxisView* dest_rtv = 0;
1536
1537                 if (rv->region()->locked() || (rv->region()->video_locked() && !_ignore_video_lock)) {
1538                         ++i;
1539                         continue;
1540                 }
1541
1542                 if (i->time_axis_view < 0 || i->time_axis_view >= (int)_time_axis_views.size()) {
1543                         /* dragged to drop zone */
1544
1545                         PlaylistMapping::iterator pm;
1546
1547                         if ((pm = playlist_mapping.find (i->view->region()->playlist())) == playlist_mapping.end()) {
1548                                 /* first region from this original playlist: create a new track */
1549                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1550                                 playlist_mapping.insert (make_pair (i->view->region()->playlist(), new_time_axis_view));
1551                                 dest_rtv = new_time_axis_view;
1552                         } else {
1553                                 /* we already created a new track for regions from this playlist, use it */
1554                                 dest_rtv = pm->second;
1555                         }
1556
1557                 } else {
1558                         /* destination time axis view is the one we dragged to */
1559                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1560                 }
1561
1562                 assert (dest_rtv);
1563
1564                 double const dest_layer = i->layer;
1565
1566                 views_to_update.insert (dest_rtv);
1567
1568                 framepos_t where;
1569
1570                 if (changed_position && !_x_constrained) {
1571                         where = rv->region()->position() - drag_delta;
1572                 } else {
1573                         where = rv->region()->position();
1574                 }
1575
1576                 if (changed_tracks) {
1577
1578                         /* insert into new playlist */
1579
1580                         RegionView* new_view = insert_region_into_playlist (
1581                                 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where,
1582                                 modified_playlists, _editor->get_grid_music_divisions (ev_state)
1583                                 );
1584
1585                         if (new_view == 0) {
1586                                 ++i;
1587                                 continue;
1588                         }
1589
1590                         new_views.push_back (new_view);
1591
1592                         /* remove from old playlist */
1593
1594                         /* the region that used to be in the old playlist is not
1595                            moved to the new one - we use a copy of it. as a result,
1596                            any existing editor for the region should no longer be
1597                            visible.
1598                         */
1599                         rv->hide_region_editor();
1600
1601
1602                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1603
1604                 } else {
1605
1606                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1607
1608                         /* this movement may result in a crossfade being modified, or a layering change,
1609                            so we need to get undo data from the playlist as well as the region.
1610                         */
1611
1612                         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1613                         if (r.second) {
1614                                 playlist->clear_changes ();
1615                         }
1616
1617                         rv->region()->clear_changes ();
1618
1619                         /*
1620                            motion on the same track. plonk the previously reparented region
1621                            back to its original canvas group (its streamview).
1622                            No need to do anything for copies as they are fake regions which will be deleted.
1623                         */
1624
1625                         rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1626                         rv->get_canvas_group()->set_y_position (i->initial_y);
1627                         rv->drag_end ();
1628
1629                         /* just change the model */
1630                         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1631                                 playlist->set_layer (rv->region(), dest_layer);
1632                         }
1633
1634                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1635
1636                         r = frozen_playlists.insert (playlist);
1637
1638                         if (r.second) {
1639                                 playlist->freeze ();
1640                         }
1641
1642                         rv->region()->set_position (where, _editor->get_grid_music_divisions (ev_state));
1643                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1644                 }
1645
1646                 if (changed_tracks) {
1647
1648                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1649                            was selected in all of them, then removing it from a playlist will have removed all
1650                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1651                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1652                            corresponding regionview, and _views is now empty).
1653
1654                            This could have invalidated any and all iterators into _views.
1655
1656                            The heuristic we use here is: if the region selection is empty, break out of the loop
1657                            here. if the region selection is not empty, then restart the loop because we know that
1658                            we must have removed at least the region(view) we've just been working on as well as any
1659                            that we processed on previous iterations.
1660
1661                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1662                            we can just iterate.
1663                         */
1664
1665
1666                         if (_views.empty()) {
1667                                 break;
1668                         } else {
1669                                 i = _views.begin();
1670                         }
1671
1672                 } else {
1673                         ++i;
1674                 }
1675         }
1676
1677         /* If we've created new regions either by copying or moving
1678            to a new track, we want to replace the old selection with the new ones
1679         */
1680
1681         if (new_views.size() > 0) {
1682                 _editor->selection->set (new_views);
1683         }
1684
1685         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1686                 (*p)->thaw();
1687         }
1688
1689         /* write commands for the accumulated diffs for all our modified playlists */
1690         add_stateful_diff_commands_for_playlists (modified_playlists);
1691         /* applies to _brushing */
1692         _editor->commit_reversible_command ();
1693
1694         /* We have futzed with the layering of canvas items on our streamviews.
1695            If any region changed layer, this will have resulted in the stream
1696            views being asked to set up their region views, and all will be well.
1697            If not, we might now have badly-ordered region views.  Ask the StreamViews
1698            involved to sort themselves out, just in case.
1699         */
1700
1701         for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1702                 (*i)->view()->playlist_layered ((*i)->track ());
1703         }
1704 }
1705
1706 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1707  *  @param region Region to remove.
1708  *  @param playlist playlist To remove from.
1709  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1710  *  that clear_changes () is only called once per playlist.
1711  */
1712 void
1713 RegionMoveDrag::remove_region_from_playlist (
1714         boost::shared_ptr<Region> region,
1715         boost::shared_ptr<Playlist> playlist,
1716         PlaylistSet& modified_playlists
1717         )
1718 {
1719         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1720
1721         if (r.second) {
1722                 playlist->clear_changes ();
1723         }
1724
1725         playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1726 }
1727
1728
1729 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1730  *  clearing the playlist's diff history first if necessary.
1731  *  @param region Region to insert.
1732  *  @param dest_rtv Destination RouteTimeAxisView.
1733  *  @param dest_layer Destination layer.
1734  *  @param where Destination position.
1735  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1736  *  that clear_changes () is only called once per playlist.
1737  *  @return New RegionView, or 0 if no insert was performed.
1738  */
1739 RegionView *
1740 RegionMoveDrag::insert_region_into_playlist (
1741         boost::shared_ptr<Region> region,
1742         RouteTimeAxisView* dest_rtv,
1743         layer_t dest_layer,
1744         framecnt_t where,
1745         PlaylistSet& modified_playlists,
1746         const int32_t sub_num
1747         )
1748 {
1749         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1750         if (!dest_playlist) {
1751                 return 0;
1752         }
1753
1754         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1755         _new_region_view = 0;
1756         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1757
1758         /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1759         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1760         if (r.second) {
1761                 dest_playlist->clear_changes ();
1762         }
1763         dest_playlist->add_region (region, where, 1.0, false, sub_num);
1764
1765         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1766                 dest_playlist->set_layer (region, dest_layer);
1767         }
1768
1769         c.disconnect ();
1770
1771         assert (_new_region_view);
1772
1773         return _new_region_view;
1774 }
1775
1776 void
1777 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1778 {
1779         _new_region_view = rv;
1780 }
1781
1782 void
1783 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1784 {
1785         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1786                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1787                 if (!c->empty()) {
1788                         _editor->session()->add_command (c);
1789                 } else {
1790                         delete c;
1791                 }
1792         }
1793 }
1794
1795
1796 void
1797 RegionMoveDrag::aborted (bool movement_occurred)
1798 {
1799         if (_copy) {
1800
1801                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1802                         list<DraggingView>::const_iterator next = i;
1803                         ++next;
1804                         delete i->view;
1805                         i = next;
1806                 }
1807
1808                 _views.clear ();
1809
1810         } else {
1811                 RegionMotionDrag::aborted (movement_occurred);
1812         }
1813 }
1814
1815 void
1816 RegionMotionDrag::aborted (bool)
1817 {
1818         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1819
1820                 StreamView* sview = (*i)->view();
1821
1822                 if (sview) {
1823                         if (sview->layer_display() == Expanded) {
1824                                 sview->set_layer_display (Stacked);
1825                         }
1826                 }
1827         }
1828
1829         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1830                 RegionView* rv = i->view;
1831                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1832                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1833                 assert (rtv);
1834                 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1835                 rv->get_canvas_group()->set_y_position (0);
1836                 rv->drag_end ();
1837                 rv->move (-_total_x_delta, 0);
1838                 rv->set_height (rtv->view()->child_height ());
1839         }
1840 }
1841
1842 /** @param b true to brush, otherwise false.
1843  *  @param c true to make copies of the regions being moved, otherwise false.
1844  */
1845 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1846         : RegionMotionDrag (e, i, p, v, b)
1847         , _copy (c)
1848         , _new_region_view (0)
1849 {
1850         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1851
1852         double speed = 1;
1853         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1854         if (rtv && rtv->is_track()) {
1855                 speed = rtv->track()->speed ();
1856         }
1857
1858         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1859 }
1860
1861 void
1862 RegionMoveDrag::setup_pointer_frame_offset ()
1863 {
1864         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1865 }
1866
1867 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1868         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1869 {
1870         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1871
1872         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1873                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1874
1875         _primary = v->view()->create_region_view (r, false, false);
1876
1877         _primary->get_canvas_group()->show ();
1878         _primary->set_position (pos, 0);
1879         _views.push_back (DraggingView (_primary, this, v));
1880
1881         _last_frame_position = pos;
1882
1883         _item = _primary->get_canvas_group ();
1884 }
1885
1886 void
1887 RegionInsertDrag::finished (GdkEvent * event, bool)
1888 {
1889         int pos = _views.front().time_axis_view;
1890         assert(pos >= 0 && pos < (int)_time_axis_views.size());
1891
1892         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[pos]);
1893
1894         _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1895         _primary->get_canvas_group()->set_y_position (0);
1896
1897         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1898
1899         _editor->begin_reversible_command (Operations::insert_region);
1900         playlist->clear_changes ();
1901         playlist->add_region (_primary->region (), _last_frame_position);
1902
1903         // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1904         if (Config->get_edit_mode() == Ripple) {
1905                 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1906         }
1907
1908         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1909         _editor->commit_reversible_command ();
1910
1911         delete _primary;
1912         _primary = 0;
1913         _views.clear ();
1914 }
1915
1916 void
1917 RegionInsertDrag::aborted (bool)
1918 {
1919         delete _primary;
1920         _primary = 0;
1921         _views.clear ();
1922 }
1923
1924 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1925         : RegionMoveDrag (e, i, p, v, false, false)
1926 {
1927         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1928 }
1929
1930 struct RegionSelectionByPosition {
1931         bool operator() (RegionView*a, RegionView* b) {
1932                 return a->region()->position () < b->region()->position();
1933         }
1934 };
1935
1936 void
1937 RegionSpliceDrag::motion (GdkEvent* event, bool)
1938 {
1939         /* Which trackview is this ? */
1940
1941         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1942         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1943
1944         /* The region motion is only processed if the pointer is over
1945            an audio track.
1946         */
1947
1948         if (!tv || !tv->is_track()) {
1949                 /* To make sure we hide the verbose canvas cursor when the mouse is
1950                    not held over an audio track.
1951                 */
1952                 _editor->verbose_cursor()->hide ();
1953                 return;
1954         } else {
1955                 _editor->verbose_cursor()->show ();
1956         }
1957
1958         int dir;
1959
1960         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1961                 dir = 1;
1962         } else {
1963                 dir = -1;
1964         }
1965
1966         RegionSelection copy;
1967         _editor->selection->regions.by_position(copy);
1968
1969         framepos_t const pf = adjusted_current_frame (event);
1970
1971         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1972
1973                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1974
1975                 if (!atv) {
1976                         continue;
1977                 }
1978
1979                 boost::shared_ptr<Playlist> playlist;
1980
1981                 if ((playlist = atv->playlist()) == 0) {
1982                         continue;
1983                 }
1984
1985                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1986                         continue;
1987                 }
1988
1989                 if (dir > 0) {
1990                         if (pf < (*i)->region()->last_frame() + 1) {
1991                                 continue;
1992                         }
1993                 } else {
1994                         if (pf > (*i)->region()->first_frame()) {
1995                                 continue;
1996                         }
1997                 }
1998
1999
2000                 playlist->shuffle ((*i)->region(), dir);
2001         }
2002 }
2003
2004 void
2005 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
2006 {
2007         RegionMoveDrag::finished (event, movement_occurred);
2008 }
2009
2010 void
2011 RegionSpliceDrag::aborted (bool)
2012 {
2013         /* XXX: TODO */
2014 }
2015
2016 /***
2017  * ripple mode...
2018  */
2019
2020 void
2021 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
2022 {
2023
2024         boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
2025
2026         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
2027         RegionSelection to_ripple;
2028         for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
2029                 if ((*i)->position() >= where) {
2030                         to_ripple.push_back (rtv->view()->find_view(*i));
2031                 }
2032         }
2033
2034         for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
2035                 if (!exclude.contains (*i)) {
2036                         // the selection has already been added to _views
2037
2038                         if (drag_in_progress) {
2039                                 // do the same things that RegionMotionDrag::motion does when
2040                                 // first_move is true, for the region views that we're adding
2041                                 // to _views this time
2042
2043                                 (*i)->drag_start();
2044                                 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
2045                                 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
2046                                 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
2047                                 rvg->reparent (_editor->_drag_motion_group);
2048
2049                                 // we only need to move in the y direction
2050                                 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
2051                                 fudge.x = 0;
2052                                 rvg->move (fudge);
2053
2054                         }
2055                         _views.push_back (DraggingView (*i, this, tav));
2056                 }
2057         }
2058 }
2059
2060 void
2061 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
2062 {
2063
2064         for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
2065                 // we added all the regions after the selection
2066
2067                 std::list<DraggingView>::iterator to_erase = i++;
2068                 if (!_editor->selection->regions.contains (to_erase->view)) {
2069                         // restore the non-selected regions to their original playlist & positions,
2070                         // and then ripple them back by the length of the regions that were dragged away
2071                         // do the same things as RegionMotionDrag::aborted
2072
2073                         RegionView *rv = to_erase->view;
2074                         TimeAxisView* tv = &(rv->get_time_axis_view ());
2075                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
2076                         assert (rtv);
2077
2078                         // plonk them back onto their own track
2079                         rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
2080                         rv->get_canvas_group()->set_y_position (0);
2081                         rv->drag_end ();
2082
2083                         if (move_regions) {
2084                                 // move the underlying region to match the view
2085                                 rv->region()->set_position (rv->region()->position() + amount);
2086                         } else {
2087                                 // restore the view to match the underlying region's original position
2088                                 rv->move(-amount, 0);   // second parameter is y delta - seems 0 is OK
2089                         }
2090
2091                         rv->set_height (rtv->view()->child_height ());
2092                         _views.erase (to_erase);
2093                 }
2094         }
2095 }
2096
2097 bool
2098 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer, int skip_invisible) const
2099 {
2100         if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer, skip_invisible)) {
2101                 if (delta_track) {
2102                         return allow_moves_across_tracks;
2103                 } else {
2104                         return true;
2105                 }
2106         }
2107         return false;
2108 }
2109
2110 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2111         : RegionMoveDrag (e, i, p, v, false, false)
2112 {
2113         DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
2114         // compute length of selection
2115         RegionSelection selected_regions = _editor->selection->regions;
2116         selection_length = selected_regions.end_frame() - selected_regions.start();
2117
2118         // we'll only allow dragging to another track in ripple mode if all the regions
2119         // being dragged start off on the same track
2120         allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
2121         prev_tav = NULL;
2122         prev_amount = 0;
2123         exclude = new RegionList;
2124         for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
2125                 exclude->push_back((*i)->region());
2126         }
2127
2128         // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
2129         RegionSelection copy;
2130         selected_regions.by_position(copy); // get selected regions sorted by position into copy
2131
2132         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
2133         std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2134
2135         for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2136                 // find ripple start point on each applicable playlist
2137                 RegionView *first_selected_on_this_track = NULL;
2138                 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
2139                         if ((*i)->region()->playlist() == (*pi)) {
2140                                 // region is on this playlist - it's the first, because they're sorted
2141                                 first_selected_on_this_track = *i;
2142                                 break;
2143                         }
2144                 }
2145                 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
2146                 add_all_after_to_views (
2147                                 &first_selected_on_this_track->get_time_axis_view(),
2148                                 first_selected_on_this_track->region()->position(),
2149                                 selected_regions, false);
2150         }
2151
2152         if (allow_moves_across_tracks) {
2153                 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
2154         } else {
2155                 orig_tav = NULL;
2156         }
2157
2158 }
2159
2160 void
2161 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
2162 {
2163         /* Which trackview is this ? */
2164
2165         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
2166         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2167
2168         /* The region motion is only processed if the pointer is over
2169            an audio track.
2170          */
2171
2172         if (!tv || !tv->is_track()) {
2173                 /* To make sure we hide the verbose canvas cursor when the mouse is
2174                    not held over an audiotrack.
2175                  */
2176                 _editor->verbose_cursor()->hide ();
2177                 return;
2178         }
2179
2180         framepos_t where = adjusted_current_frame (event);
2181         assert (where >= 0);
2182         framepos_t after;
2183         double delta = compute_x_delta (event, &after);
2184
2185         framecnt_t amount = _editor->pixel_to_sample (delta);
2186
2187         if (allow_moves_across_tracks) {
2188                 // all the originally selected regions were on the same track
2189
2190                 framecnt_t adjust = 0;
2191                 if (prev_tav && tv != prev_tav) {
2192                         // dragged onto a different track
2193                         // remove the unselected regions from _views, restore them to their original positions
2194                         // and add the regions after the drop point on the new playlist to _views instead.
2195                         // undo the effect of rippling the previous playlist, and include the effect of removing
2196                         // the dragged region(s) from this track
2197
2198                         remove_unselected_from_views (prev_amount, false);
2199                         // ripple previous playlist according to the regions that have been removed onto the new playlist
2200                         prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
2201                         prev_amount = 0;
2202
2203                         // move just the selected regions
2204                         RegionMoveDrag::motion(event, first_move);
2205
2206                         // ensure that the ripple operation on the new playlist inserts selection_length time
2207                         adjust = selection_length;
2208                         // ripple the new current playlist
2209                         tv->playlist()->ripple (where, amount+adjust, exclude);
2210
2211                         // add regions after point where drag entered this track to subsequent ripples
2212                         add_all_after_to_views (tv, where, _editor->selection->regions, true);
2213
2214                 } else {
2215                         // motion on same track
2216                         RegionMoveDrag::motion(event, first_move);
2217                 }
2218                 prev_tav = tv;
2219
2220                 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
2221                 prev_position = where;
2222         } else {
2223                 // selection encompasses multiple tracks - just drag
2224                 // cross-track drags are forbidden
2225                 RegionMoveDrag::motion(event, first_move);
2226         }
2227
2228         if (!_x_constrained) {
2229                 prev_amount += amount;
2230         }
2231
2232         _last_frame_position = after;
2233 }
2234
2235 void
2236 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
2237 {
2238         if (!movement_occurred) {
2239
2240                 /* just a click */
2241
2242                 if (was_double_click() && !_views.empty()) {
2243                         DraggingView dv = _views.front();
2244                         dv.view->show_region_editor ();
2245
2246                 }
2247
2248                 return;
2249         }
2250
2251         _editor->begin_reversible_command(_("Ripple drag"));
2252
2253         // remove the regions being rippled from the dragging view, updating them to
2254         // their new positions
2255         remove_unselected_from_views (prev_amount, true);
2256
2257         if (allow_moves_across_tracks) {
2258                 if (orig_tav) {
2259                         // if regions were dragged across tracks, we've rippled any later
2260                         // regions on the track the regions were dragged off, so we need
2261                         // to add the original track to the undo record
2262                         orig_tav->playlist()->clear_changes();
2263                         vector<Command*> cmds;
2264                         orig_tav->playlist()->rdiff (cmds);
2265                         _editor->session()->add_commands (cmds);
2266                 }
2267                 if (prev_tav && prev_tav != orig_tav) {
2268                         prev_tav->playlist()->clear_changes();
2269                         vector<Command*> cmds;
2270                         prev_tav->playlist()->rdiff (cmds);
2271                         _editor->session()->add_commands (cmds);
2272                 }
2273         } else {
2274                 // selection spanned multiple tracks - all will need adding to undo record
2275
2276                 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
2277                 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
2278
2279                 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
2280                         (*pi)->clear_changes();
2281                         vector<Command*> cmds;
2282                         (*pi)->rdiff (cmds);
2283                         _editor->session()->add_commands (cmds);
2284                 }
2285         }
2286
2287         // other modified playlists are added to undo by RegionMoveDrag::finished()
2288         RegionMoveDrag::finished (event, movement_occurred);
2289         _editor->commit_reversible_command();
2290 }
2291
2292 void
2293 RegionRippleDrag::aborted (bool movement_occurred)
2294 {
2295         RegionMoveDrag::aborted (movement_occurred);
2296         _views.clear ();
2297 }
2298
2299
2300 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
2301         : Drag (e, i),
2302           _view (dynamic_cast<MidiTimeAxisView*> (v))
2303 {
2304         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
2305
2306         assert (_view);
2307 }
2308
2309 void
2310 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
2311 {
2312         if (first_move) {
2313                 _editor->begin_reversible_command (_("create region"));
2314                 _region = add_midi_region (_view, false, _editor->get_grid_music_divisions (event->button.state));
2315                 _view->playlist()->freeze ();
2316         } else {
2317                 if (_region) {
2318                         framepos_t const f = adjusted_current_frame (event);
2319                         if (f < grab_frame()) {
2320                                 _region->set_initial_position (f);
2321                         }
2322
2323                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
2324                            so that if this region is duplicated, its duplicate starts on
2325                            a snap point rather than 1 frame after a snap point.  Otherwise things get
2326                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
2327                            place snapped notes at the start of the region.
2328                         */
2329
2330                         framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
2331                         _region->set_length (len < 1 ? 1 : len, _editor->get_grid_music_divisions (event->button.state));
2332                 }
2333         }
2334 }
2335
2336 void
2337 RegionCreateDrag::finished (GdkEvent* event, bool movement_occurred)
2338 {
2339         if (!movement_occurred) {
2340                 add_midi_region (_view, true, _editor->get_grid_music_divisions (event->button.state));
2341         } else {
2342                 _view->playlist()->thaw ();
2343                 _editor->commit_reversible_command();
2344         }
2345 }
2346
2347 void
2348 RegionCreateDrag::aborted (bool)
2349 {
2350         if (_region) {
2351                 _view->playlist()->thaw ();
2352         }
2353
2354         /* XXX */
2355 }
2356
2357 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
2358         : Drag (e, i)
2359         , region (0)
2360         , relative (false)
2361         , at_front (true)
2362         , _was_selected (false)
2363         , _snap_delta (0)
2364 {
2365         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
2366 }
2367
2368 void
2369 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
2370 {
2371         Gdk::Cursor* cursor;
2372         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2373         assert (cnote);
2374         float x_fraction = cnote->mouse_x_fraction ();
2375
2376         if (x_fraction > 0.0 && x_fraction < 0.25) {
2377                 cursor = _editor->cursors()->left_side_trim;
2378                 at_front = true;
2379         } else  {
2380                 cursor = _editor->cursors()->right_side_trim;
2381                 at_front = false;
2382         }
2383
2384         Drag::start_grab (event, cursor);
2385
2386         region = &cnote->region_view();
2387
2388         double temp;
2389         temp = region->snap_to_pixel (cnote->x0 (), true);
2390         _snap_delta = temp - cnote->x0 ();
2391
2392         _item->grab ();
2393
2394         if (event->motion.state & ArdourKeyboard::note_size_relative_modifier ()) {
2395                 relative = false;
2396         } else {
2397                 relative = true;
2398         }
2399         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2400         if (ms.size() > 1) {
2401                 /* has to be relative, may make no sense otherwise */
2402                 relative = true;
2403         }
2404
2405         if (!(_was_selected = cnote->selected())) {
2406
2407                 /* tertiary-click means extend selection - we'll do that on button release,
2408                    so don't add it here, because otherwise we make it hard to figure
2409                    out the "extend-to" range.
2410                 */
2411
2412                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2413
2414                 if (!extend) {
2415                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2416
2417                         if (add) {
2418                                 region->note_selected (cnote, true);
2419                         } else {
2420                                 _editor->get_selection().clear_points();
2421                                 region->unique_select (cnote);
2422                         }
2423                 }
2424         }
2425 }
2426
2427 void
2428 NoteResizeDrag::motion (GdkEvent* event, bool first_move)
2429 {
2430         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2431         if (first_move) {
2432                 _editor->begin_reversible_command (_("resize notes"));
2433
2434                 for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
2435                         MidiRegionSelection::iterator next;
2436                         next = r;
2437                         ++next;
2438                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2439                         if (mrv) {
2440                                 mrv->begin_resizing (at_front);
2441                         }
2442                         r = next;
2443                 }
2444         }
2445
2446         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2447                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2448                 assert (nb);
2449                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2450                 if (mrv) {
2451                         double sd = 0.0;
2452                         bool snap = true;
2453                         bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2454
2455                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2456                                 if (_editor->snap_mode () != SnapOff) {
2457                                         snap = false;
2458                                 }
2459                         } else {
2460                                 if (_editor->snap_mode () == SnapOff) {
2461                                         snap = false;
2462                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2463                                         if (apply_snap_delta) {
2464                                                 snap = true;
2465                                         }
2466                                 }
2467                         }
2468
2469                         if (apply_snap_delta) {
2470                                 sd = _snap_delta;
2471                         }
2472
2473                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2474                 }
2475         }
2476 }
2477
2478 void
2479 NoteResizeDrag::finished (GdkEvent* event, bool movement_occurred)
2480 {
2481         if (!movement_occurred) {
2482                 /* no motion - select note */
2483                 NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2484                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
2485                     _editor->current_mouse_mode() == Editing::MouseDraw) {
2486
2487                         bool changed = false;
2488
2489                         if (_was_selected) {
2490                                 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2491                                 if (add) {
2492                                         region->note_deselected (cnote);
2493                                         changed = true;
2494                                 } else {
2495                                         _editor->get_selection().clear_points();
2496                                         region->unique_select (cnote);
2497                                         changed = true;
2498                                 }
2499                         } else {
2500                                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
2501                                 bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
2502
2503                                 if (!extend && !add && region->selection_size() > 1) {
2504                                         _editor->get_selection().clear_points();
2505                                         region->unique_select (cnote);
2506                                         changed = true;
2507                                 } else if (extend) {
2508                                         region->note_selected (cnote, true, true);
2509                                         changed = true;
2510                                 } else {
2511                                         /* it was added during button press */
2512                                         changed = true;
2513                                 }
2514                         }
2515
2516                         if (changed) {
2517                                 _editor->begin_reversible_selection_op(X_("Resize Select Note Release"));
2518                                 _editor->commit_reversible_selection_op();
2519                         }
2520                 }
2521
2522                 return;
2523         }
2524
2525         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2526         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2527                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
2528                 assert (nb);
2529                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2530                 double sd = 0.0;
2531                 bool snap = true;
2532                 bool apply_snap_delta = ArdourKeyboard::indicates_snap_delta (event->button.state);
2533                 if (mrv) {
2534                         if (ArdourKeyboard::indicates_snap (event->button.state)) {
2535                                 if (_editor->snap_mode () != SnapOff) {
2536                                         snap = false;
2537                                 }
2538                         } else {
2539                                 if (_editor->snap_mode () == SnapOff) {
2540                                         snap = false;
2541                                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
2542                                         if (apply_snap_delta) {
2543                                                 snap = true;
2544                                         }
2545                                 }
2546                         }
2547
2548                         if (apply_snap_delta) {
2549                                 sd = _snap_delta;
2550                         }
2551
2552                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative, sd, snap);
2553                 }
2554         }
2555
2556         _editor->commit_reversible_command ();
2557 }
2558
2559 void
2560 NoteResizeDrag::aborted (bool)
2561 {
2562         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2563         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2564                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2565                 if (mrv) {
2566                         mrv->abort_resizing ();
2567                 }
2568         }
2569 }
2570
2571 AVDraggingView::AVDraggingView (RegionView* v)
2572         : view (v)
2573 {
2574         initial_position = v->region()->position ();
2575 }
2576
2577 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2578         : Drag (e, i)
2579 {
2580         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2581
2582         RegionSelection rs;
2583         TrackViewList empty;
2584         empty.clear();
2585         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2586         std::list<RegionView*> views = rs.by_layer();
2587
2588         _stuck = false;
2589         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2590                 RegionView* rv = (*i);
2591                 if (!rv->region()->video_locked()) {
2592                         continue;
2593                 }
2594                 if (rv->region()->locked()) {
2595                         _stuck = true;
2596                 }
2597                 _views.push_back (AVDraggingView (rv));
2598         }
2599 }
2600
2601 void
2602 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2603 {
2604         Drag::start_grab (event);
2605         if (_editor->session() == 0) {
2606                 return;
2607         }
2608
2609         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
2610                 _stuck = false;
2611                 _views.clear();
2612         }
2613
2614         if (_stuck) {
2615                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2616                 return;
2617         }
2618
2619         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2620         _max_backwards_drag = (
2621                           ARDOUR_UI::instance()->video_timeline->get_duration()
2622                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2623                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2624                         );
2625
2626         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2627                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2628                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2629                 }
2630         }
2631         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2632
2633         char buf[128];
2634         Timecode::Time timecode;
2635         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2636         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);
2637         show_verbose_cursor_text (buf);
2638 }
2639
2640 void
2641 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2642 {
2643         if (_editor->session() == 0) {
2644                 return;
2645         }
2646         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2647                 return;
2648         }
2649         if (_stuck) {
2650                 show_verbose_cursor_text (_("One or more Audio Regions\nare both Locked and\nLocked to Video.\nThe video cannot me moved."));
2651                 return;
2652         }
2653
2654         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2655         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2656
2657         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2658                 dt = - _max_backwards_drag;
2659         }
2660
2661         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2662         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2663
2664         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2665                 RegionView* rv = i->view;
2666                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2667                 if (first_move) {
2668                         rv->drag_start ();
2669                         rv->region()->clear_changes ();
2670                         rv->region()->suspend_property_changes();
2671                 }
2672                 rv->region()->set_position(i->initial_position + dt);
2673                 rv->region_changed(ARDOUR::Properties::position);
2674         }
2675
2676         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2677         Timecode::Time timecode;
2678         Timecode::Time timediff;
2679         char buf[128];
2680         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2681         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2682         snprintf (buf, sizeof (buf),
2683                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2684                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2685                         , _("Video Start:"),
2686                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2687                         , _("Diff:"),
2688                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2689                                 );
2690         show_verbose_cursor_text (buf);
2691 }
2692
2693 void
2694 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2695 {
2696         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2697                 return;
2698         }
2699         if (_stuck) {
2700                 return;
2701         }
2702
2703         if (!movement_occurred || ! _editor->session()) {
2704                 return;
2705         }
2706
2707         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2708
2709         _editor->begin_reversible_command (_("Move Video"));
2710
2711         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2712         ARDOUR_UI::instance()->video_timeline->save_undo();
2713         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2714         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2715
2716         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2717                 i->view->drag_end();
2718                 i->view->region()->resume_property_changes ();
2719
2720                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2721         }
2722
2723         _editor->session()->maybe_update_session_range(
2724                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2725                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2726                         );
2727
2728
2729         _editor->commit_reversible_command ();
2730 }
2731
2732 void
2733 VideoTimeLineDrag::aborted (bool)
2734 {
2735         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2736                 return;
2737         }
2738         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2739         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2740
2741         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2742                 i->view->region()->resume_property_changes ();
2743                 i->view->region()->set_position(i->initial_position);
2744         }
2745 }
2746
2747 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2748         : RegionDrag (e, i, p, v)
2749         , _operation (StartTrim)
2750         , _preserve_fade_anchor (preserve_fade_anchor)
2751         , _jump_position_when_done (false)
2752 {
2753         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2754 }
2755
2756 void
2757 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2758 {
2759         double speed = 1.0;
2760         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2761         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2762
2763         if (tv && tv->is_track()) {
2764                 speed = tv->track()->speed();
2765         }
2766
2767         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2768         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2769         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2770
2771         framepos_t const pf = adjusted_current_frame (event);
2772         setup_snap_delta (region_start);
2773
2774         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_contents_modifier ())) {
2775                 /* Move the contents of the region around without changing the region bounds */
2776                 _operation = ContentsTrim;
2777                 Drag::start_grab (event, _editor->cursors()->trimmer);
2778         } else {
2779                 /* These will get overridden for a point trim.*/
2780                 if (pf < (region_start + region_length/2)) {
2781                         /* closer to front */
2782                         _operation = StartTrim;
2783                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2784                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2785                         } else {
2786                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2787                         }
2788                 } else {
2789                         /* closer to end */
2790                         _operation = EndTrim;
2791                         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::trim_anchored_modifier ())) {
2792                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2793                         } else {
2794                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2795                         }
2796                 }
2797         }
2798         /* jump trim disabled for now
2799         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::trim_jump_modifier ())) {
2800                 _jump_position_when_done = true;
2801         }
2802         */
2803
2804         switch (_operation) {
2805         case StartTrim:
2806                 show_verbose_cursor_time (region_start);
2807                 break;
2808         case EndTrim:
2809                 show_verbose_cursor_duration (region_start, region_end);
2810                 break;
2811         case ContentsTrim:
2812                 show_verbose_cursor_time (pf);
2813                 break;
2814         }
2815
2816         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2817                 i->view->region()->suspend_property_changes ();
2818         }
2819 }
2820
2821 void
2822 TrimDrag::motion (GdkEvent* event, bool first_move)
2823 {
2824         RegionView* rv = _primary;
2825
2826         double speed = 1.0;
2827         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2828         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2829         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2830         frameoffset_t frame_delta = 0;
2831
2832         if (tv && tv->is_track()) {
2833                 speed = tv->track()->speed();
2834         }
2835         framecnt_t adj_frame = adjusted_frame (_drags->current_pointer_frame () + snap_delta (event->button.state), event, true);
2836         framecnt_t dt = adj_frame - raw_grab_frame () + _pointer_frame_offset - snap_delta (event->button.state);
2837
2838         if (first_move) {
2839
2840                 string trim_type;
2841
2842                 switch (_operation) {
2843                 case StartTrim:
2844                         trim_type = "Region start trim";
2845                         break;
2846                 case EndTrim:
2847                         trim_type = "Region end trim";
2848                         break;
2849                 case ContentsTrim:
2850                         trim_type = "Region content trim";
2851                         break;
2852                 default:
2853                         assert(0);
2854                         break;
2855                 }
2856
2857                 _editor->begin_reversible_command (trim_type);
2858
2859                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2860                         RegionView* rv = i->view;
2861                         rv->region()->playlist()->clear_owned_changes ();
2862
2863                         if (_operation == StartTrim) {
2864                                 rv->trim_front_starting ();
2865                         }
2866
2867                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2868
2869                         if (arv) {
2870                                 arv->temporarily_hide_envelope ();
2871                                 arv->drag_start ();
2872                         }
2873
2874                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2875                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2876
2877                         if (insert_result.second) {
2878                                 pl->freeze();
2879                         }
2880                 }
2881         }
2882
2883         bool non_overlap_trim = false;
2884
2885         if (event && Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::trim_overlap_modifier ())) {
2886                 non_overlap_trim = true;
2887         }
2888
2889         /* contstrain trim to fade length */
2890         if (_preserve_fade_anchor) {
2891                 switch (_operation) {
2892                         case StartTrim:
2893                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2894                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2895                                         if (!arv) continue;
2896                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2897                                         if (ar->locked()) continue;
2898                                         framecnt_t len = ar->fade_in()->back()->when;
2899                                         if (len < dt) dt = min(dt, len);
2900                                 }
2901                                 break;
2902                         case EndTrim:
2903                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2904                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2905                                         if (!arv) continue;
2906                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2907                                         if (ar->locked()) continue;
2908                                         framecnt_t len = ar->fade_out()->back()->when;
2909                                         if (len < -dt) dt = max(dt, -len);
2910                                 }
2911                                 break;
2912                         case ContentsTrim:
2913                                 break;
2914                 }
2915         }
2916
2917
2918         switch (_operation) {
2919         case StartTrim:
2920                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2921                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim
2922                                                             , _editor->get_grid_music_divisions (event->button.state));
2923
2924                         if (changed && _preserve_fade_anchor) {
2925                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2926                                 if (arv) {
2927                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2928                                         framecnt_t len = ar->fade_in()->back()->when;
2929                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2930                                         framepos_t new_length = len - diff;
2931                                         i->anchored_fade_length = min (ar->length(), new_length);
2932                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2933                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2934                                 }
2935                         }
2936                 }
2937                 break;
2938
2939         case EndTrim:
2940                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2941                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim, _editor->get_grid_music_divisions (event->button.state));
2942                         if (changed && _preserve_fade_anchor) {
2943                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2944                                 if (arv) {
2945                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2946                                         framecnt_t len = ar->fade_out()->back()->when;
2947                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2948                                         framepos_t new_length = len + diff;
2949                                         i->anchored_fade_length = min (ar->length(), new_length);
2950                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2951                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2952                                 }
2953                         }
2954                 }
2955                 break;
2956
2957         case ContentsTrim:
2958                 {
2959                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2960
2961                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2962                                 i->view->move_contents (frame_delta);
2963                         }
2964                 }
2965                 break;
2966         }
2967
2968         switch (_operation) {
2969         case StartTrim:
2970                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2971                 break;
2972         case EndTrim:
2973                 show_verbose_cursor_duration ((framepos_t)  rv->region()->position() / speed, (framepos_t) rv->region()->last_frame() / speed);
2974                 break;
2975         case ContentsTrim:
2976                 // show_verbose_cursor_time (frame_delta);
2977                 break;
2978         }
2979 }
2980
2981 void
2982 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2983 {
2984         if (movement_occurred) {
2985                 motion (event, false);
2986
2987                 if (_operation == StartTrim) {
2988                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2989                                 {
2990                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2991                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2992                                            needs to be zero in the undo record.
2993                                         */
2994                                         i->view->trim_front_ending ();
2995                                 }
2996                                 if (_preserve_fade_anchor) {
2997                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2998                                         if (arv) {
2999                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3000                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
3001                                                 ar->set_fade_in_length(i->anchored_fade_length);
3002                                                 ar->set_fade_in_active(true);
3003                                         }
3004                                 }
3005                                 if (_jump_position_when_done) {
3006                                         i->view->region()->set_position (i->initial_position);
3007                                 }
3008                         }
3009                 } else if (_operation == EndTrim) {
3010                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3011                                 if (_preserve_fade_anchor) {
3012                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
3013                                         if (arv) {
3014                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
3015                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
3016                                                 ar->set_fade_out_length(i->anchored_fade_length);
3017                                                 ar->set_fade_out_active(true);
3018                                         }
3019                                 }
3020                                 if (_jump_position_when_done) {
3021                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
3022                                 }
3023                         }
3024                 }
3025
3026                 if (!_views.empty()) {
3027                         if (_operation == StartTrim) {
3028                                 _editor->maybe_locate_with_edit_preroll(
3029                                         _views.begin()->view->region()->position());
3030                         }
3031                         if (_operation == EndTrim) {
3032                                 _editor->maybe_locate_with_edit_preroll(
3033                                         _views.begin()->view->region()->position() +
3034                                         _views.begin()->view->region()->length());
3035                         }
3036                 }
3037
3038                 if (!_editor->selection->selected (_primary)) {
3039                         _primary->thaw_after_trim ();
3040                 } else {
3041
3042                         set<boost::shared_ptr<Playlist> > diffed_playlists;
3043
3044                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3045                                 i->view->thaw_after_trim ();
3046                                 i->view->enable_display (true);
3047
3048                                 /* Trimming one region may affect others on the playlist, so we need
3049                                    to get undo Commands from the whole playlist rather than just the
3050                                    region.  Use diffed_playlists to make sure we don't diff a given
3051                                    playlist more than once.
3052                                 */
3053                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
3054                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
3055                                         vector<Command*> cmds;
3056                                         p->rdiff (cmds);
3057                                         _editor->session()->add_commands (cmds);
3058                                         diffed_playlists.insert (p);
3059                                 }
3060                         }
3061                 }
3062
3063                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
3064                         (*p)->thaw ();
3065                 }
3066
3067                 _editor->motion_frozen_playlists.clear ();
3068                 _editor->commit_reversible_command();
3069
3070         } else {
3071                 /* no mouse movement */
3072                 if (adjusted_current_frame (event) != adjusted_frame (_drags->current_pointer_frame(), event, false)) {
3073                         _editor->point_trim (event, adjusted_current_frame (event));
3074                 }
3075         }
3076
3077         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3078                 i->view->region()->resume_property_changes ();
3079         }
3080 }
3081
3082 void
3083 TrimDrag::aborted (bool movement_occurred)
3084 {
3085         /* Our motion method is changing model state, so use the Undo system
3086            to cancel.  Perhaps not ideal, as this will leave an Undo point
3087            behind which may be slightly odd from the user's point of view.
3088         */
3089
3090         GdkEvent ev;
3091         finished (&ev, true);
3092
3093         if (movement_occurred) {
3094                 _editor->session()->undo (1);
3095         }
3096
3097         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
3098                 i->view->region()->resume_property_changes ();
3099         }
3100 }
3101
3102 void
3103 TrimDrag::setup_pointer_frame_offset ()
3104 {
3105         list<DraggingView>::iterator i = _views.begin ();
3106         while (i != _views.end() && i->view != _primary) {
3107                 ++i;
3108         }
3109
3110         if (i == _views.end()) {
3111                 return;
3112         }
3113
3114         switch (_operation) {
3115         case StartTrim:
3116                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
3117                 break;
3118         case EndTrim:
3119                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
3120                 break;
3121         case ContentsTrim:
3122                 break;
3123         }
3124 }
3125
3126 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3127         : Drag (e, i)
3128         , _copy (c)
3129         , _old_snap_type (e->snap_type())
3130         , _old_snap_mode (e->snap_mode())
3131         , before_state (0)
3132 {
3133         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
3134         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
3135         assert (_marker);
3136         _real_section = &_marker->meter();
3137
3138 }
3139
3140 void
3141 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3142 {
3143         Drag::start_grab (event, cursor);
3144         show_verbose_cursor_time (adjusted_current_frame(event));
3145 }
3146
3147 void
3148 MeterMarkerDrag::setup_pointer_frame_offset ()
3149 {
3150         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
3151 }
3152
3153 void
3154 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
3155 {
3156         if (first_move) {
3157                 // create a dummy marker to catch events, then hide it.
3158
3159                 char name[64];
3160                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
3161
3162                 _marker = new MeterMarker (
3163                         *_editor,
3164                         *_editor->meter_group,
3165                         UIConfiguration::instance().color ("meter marker"),
3166                         name,
3167                         *new MeterSection (_marker->meter())
3168                 );
3169
3170                 /* use the new marker for the grab */
3171                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3172                 _marker->hide();
3173
3174                 TempoMap& map (_editor->session()->tempo_map());
3175                 /* get current state */
3176                 before_state = &map.get_state();
3177
3178                 if (!_copy) {
3179                         _editor->begin_reversible_command (_("move meter mark"));
3180                 } else {
3181                         _editor->begin_reversible_command (_("copy meter mark"));
3182
3183                         Timecode::BBT_Time bbt = _real_section->bbt();
3184
3185                         /* we can't add a meter where one currently exists */
3186                         if (_real_section->frame() < adjusted_current_frame (event, false)) {
3187                                 ++bbt.bars;
3188                         } else {
3189                                 --bbt.bars;
3190                         }
3191                         const double beat = map.beat_at_bbt (bbt);
3192                         _real_section = map.add_meter (Meter (_marker->meter().divisions_per_bar(), _marker->meter().note_divisor())
3193                                                        , beat, bbt, map.frame_at_bbt (bbt), _real_section->position_lock_style());
3194                         if (!_real_section) {
3195                                 aborted (true);
3196                                 return;
3197                         }
3198
3199                 }
3200                 /* only snap to bars. leave snap mode alone for audio locked meters.*/
3201                 if (_real_section->position_lock_style() != AudioTime) {
3202                         _editor->set_snap_to (SnapToBar);
3203                         _editor->set_snap_mode (SnapNormal);
3204                 }
3205         }
3206
3207         framepos_t pf = adjusted_current_frame (event);
3208
3209         if (_real_section->position_lock_style() == AudioTime && _editor->snap_musical()) {
3210                 /* never snap to music for audio locked */
3211                 pf = adjusted_current_frame (event, false);
3212         }
3213
3214         _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
3215
3216         /* fake marker meeds to stay under the mouse, unlike the real one. */
3217         _marker->set_position (adjusted_current_frame (event, false));
3218
3219         show_verbose_cursor_time (_real_section->frame());
3220 }
3221
3222 void
3223 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3224 {
3225         if (!movement_occurred) {
3226                 if (was_double_click()) {
3227                         _editor->edit_meter_marker (*_marker);
3228                 }
3229                 return;
3230         }
3231
3232         /* reinstate old snap setting */
3233         _editor->set_snap_to (_old_snap_type);
3234         _editor->set_snap_mode (_old_snap_mode);
3235
3236         TempoMap& map (_editor->session()->tempo_map());
3237
3238         XMLNode &after = map.get_state();
3239         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3240         _editor->commit_reversible_command ();
3241
3242         // delete the dummy marker we used for visual representation while moving.
3243         // a new visual marker will show up automatically.
3244         delete _marker;
3245 }
3246
3247 void
3248 MeterMarkerDrag::aborted (bool moved)
3249 {
3250         _marker->set_position (_marker->meter().frame ());
3251         if (moved) {
3252                 /* reinstate old snap setting */
3253                 _editor->set_snap_to (_old_snap_type);
3254                 _editor->set_snap_mode (_old_snap_mode);
3255
3256                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3257                 // delete the dummy marker we used for visual representation while moving.
3258                 // a new visual marker will show up automatically.
3259                 delete _marker;
3260         }
3261 }
3262
3263 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
3264         : Drag (e, i)
3265         , _copy (c)
3266         , before_state (0)
3267 {
3268         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
3269
3270         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
3271         _real_section = &_marker->tempo();
3272         _movable = _real_section->movable();
3273         assert (_marker);
3274 }
3275
3276 void
3277 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3278 {
3279         Drag::start_grab (event, cursor);
3280         if (!_real_section->active()) {
3281                 show_verbose_cursor_text (_("inactive"));
3282         } else {
3283                 show_verbose_cursor_time (adjusted_current_frame (event));
3284         }
3285 }
3286
3287 void
3288 TempoMarkerDrag::setup_pointer_frame_offset ()
3289 {
3290         _pointer_frame_offset = raw_grab_frame() - _real_section->frame();
3291 }
3292
3293 void
3294 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
3295 {
3296         if (!_real_section->active()) {
3297                 return;
3298         }
3299
3300         if (first_move) {
3301
3302                 // mvc drag - create a dummy marker to catch events, hide it.
3303
3304                 char name[64];
3305                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
3306
3307                 TempoSection section (_marker->tempo());
3308
3309                 _marker = new TempoMarker (
3310                         *_editor,
3311                         *_editor->tempo_group,
3312                         UIConfiguration::instance().color ("tempo marker"),
3313                         name,
3314                         *new TempoSection (_marker->tempo())
3315                         );
3316
3317                 /* use the new marker for the grab */
3318                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
3319                 _marker->hide();
3320
3321                 TempoMap& map (_editor->session()->tempo_map());
3322                 /* get current state */
3323                 before_state = &map.get_state();
3324
3325                 if (!_copy) {
3326                         _editor->begin_reversible_command (_("move tempo mark"));
3327
3328                 } else {
3329                         const Tempo tempo (_marker->tempo());
3330                         const framepos_t frame = adjusted_current_frame (event) + 1;
3331                         const TempoSection::Type type = _real_section->type();
3332
3333                         _editor->begin_reversible_command (_("copy tempo mark"));
3334
3335                         if (_real_section->position_lock_style() == MusicTime) {
3336                                 _real_section = map.add_tempo (tempo, map.pulse_at_frame (frame), 0, type, MusicTime);
3337                         } else {
3338                                 _real_section = map.add_tempo (tempo, 0.0, frame, type, AudioTime);
3339                         }
3340
3341                         if (!_real_section) {
3342                                 aborted (true);
3343                                 return;
3344                         }
3345                 }
3346
3347         }
3348
3349         if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3350                 /* use vertical movement to alter tempo .. should be log */
3351                 double new_bpm = _real_section->beats_per_minute() + ((last_pointer_y() - current_pointer_y()) / 5.0);
3352                 stringstream strs;
3353
3354                 _editor->session()->tempo_map().gui_change_tempo (_real_section, Tempo (new_bpm, _real_section->note_type()));
3355                 strs << new_bpm;
3356                 show_verbose_cursor_text (strs.str());
3357
3358         } else if (_movable && !_real_section->locked_to_meter()) {
3359                 framepos_t pf;
3360
3361                 if (_editor->snap_musical()) {
3362                         /* we can't snap to a grid that we are about to move.
3363                          * gui_move_tempo() will sort out snap using the supplied beat divisions.
3364                         */
3365                         pf = adjusted_current_frame (event, false);
3366                 } else {
3367                         pf = adjusted_current_frame (event);
3368                 }
3369
3370                 TempoMap& map (_editor->session()->tempo_map());
3371
3372                 /* snap to beat is 1, snap to bar is -1 (sorry) */
3373                 const int sub_num = _editor->get_grid_music_divisions (event->button.state);
3374
3375                 map.gui_move_tempo (_real_section, pf, sub_num);
3376
3377                 show_verbose_cursor_time (_real_section->frame());
3378         }
3379         _marker->set_position (adjusted_current_frame (event, false));
3380 }
3381
3382 void
3383 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3384 {
3385         if (!_real_section->active()) {
3386                 return;
3387         }
3388         if (!movement_occurred) {
3389                 if (was_double_click()) {
3390                         _editor->edit_tempo_marker (*_marker);
3391                 }
3392                 return;
3393         }
3394
3395         TempoMap& map (_editor->session()->tempo_map());
3396
3397         XMLNode &after = map.get_state();
3398         _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
3399         _editor->commit_reversible_command ();
3400
3401         // delete the dummy marker we used for visual representation while moving.
3402         // a new visual marker will show up automatically.
3403         delete _marker;
3404 }
3405
3406 void
3407 TempoMarkerDrag::aborted (bool moved)
3408 {
3409         _marker->set_position (_marker->tempo().frame());
3410         if (moved) {
3411                 TempoMap& map (_editor->session()->tempo_map());
3412                 map.set_state (*before_state, Stateful::current_state_version);
3413                 // delete the dummy (hidden) marker we used for events while moving.
3414                 delete _marker;
3415         }
3416 }
3417
3418 BBTRulerDrag::BBTRulerDrag (Editor* e, ArdourCanvas::Item* i)
3419         : Drag (e, i)
3420         , _pulse (0.0)
3421         , _tempo (0)
3422         , before_state (0)
3423 {
3424         DEBUG_TRACE (DEBUG::Drags, "New BBTRulerDrag\n");
3425
3426 }
3427
3428 void
3429 BBTRulerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3430 {
3431         Drag::start_grab (event, cursor);
3432         TempoMap& map (_editor->session()->tempo_map());
3433         _tempo = const_cast<TempoSection*> (&map.tempo_section_at_frame (raw_grab_frame()));
3434         ostringstream sstr;
3435
3436         sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (adjusted_current_frame (event)).beats_per_minute() << "\n";
3437         sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3438         show_verbose_cursor_text (sstr.str());
3439         finished (event, false);
3440 }
3441
3442 void
3443 BBTRulerDrag::setup_pointer_frame_offset ()
3444 {
3445         TempoMap& map (_editor->session()->tempo_map());
3446         const double beat_at_frame = max (0.0, map.beat_at_frame (raw_grab_frame()));
3447         const uint32_t divisions = _editor->get_grid_beat_divisions (0);
3448         double beat = 0.0;
3449
3450         if (divisions > 0) {
3451                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * divisions)) / divisions);
3452         } else {
3453                 /* while it makes some sense for the user to determine the division to 'grab',
3454                    grabbing a bar often leads to confusing results wrt the actual tempo section being altered
3455                    and the result over steep tempo curves. Use sixteenths.
3456                 */
3457                 beat = floor (beat_at_frame) + (floor (((beat_at_frame - floor (beat_at_frame)) * 4)) / 4);
3458         }
3459
3460         _pulse = map.pulse_at_beat (beat);
3461
3462         _pointer_frame_offset = raw_grab_frame() - map.frame_at_pulse (_pulse);
3463
3464 }
3465
3466 void
3467 BBTRulerDrag::motion (GdkEvent* event, bool first_move)
3468 {
3469         TempoMap& map (_editor->session()->tempo_map());
3470
3471         if (first_move) {
3472                 /* get current state */
3473                 before_state = &map.get_state();
3474                 _editor->begin_reversible_command (_("dilate tempo"));
3475         }
3476
3477         framepos_t pf;
3478
3479         if (_editor->snap_musical()) {
3480                 pf = adjusted_current_frame (event, false);
3481         } else {
3482                 pf = adjusted_current_frame (event);
3483         }
3484
3485         if (ArdourKeyboard::indicates_constraint (event->button.state)) {
3486                 /* adjust previous tempo to match pointer frame */
3487                 _editor->session()->tempo_map().gui_dilate_tempo (_tempo, map.frame_at_pulse (_pulse), pf, _pulse);
3488         }
3489         ostringstream sstr;
3490         sstr << "^" << fixed << setprecision(3) << map.tempo_at_frame (pf).beats_per_minute() << "\n";
3491         sstr << "<" << fixed << setprecision(3) << _tempo->beats_per_minute();
3492         show_verbose_cursor_text (sstr.str());
3493 }
3494
3495 void
3496 BBTRulerDrag::finished (GdkEvent* event, bool movement_occurred)
3497 {
3498         if (!movement_occurred) {
3499                 return;
3500         }
3501
3502         TempoMap& map (_editor->session()->tempo_map());
3503
3504         XMLNode &after = map.get_state();
3505         _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
3506         _editor->commit_reversible_command ();
3507 }
3508
3509 void
3510 BBTRulerDrag::aborted (bool moved)
3511 {
3512         if (moved) {
3513                 _editor->session()->tempo_map().set_state (*before_state, Stateful::current_state_version);
3514         }
3515 }
3516
3517
3518 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
3519         : Drag (e, &c.track_canvas_item(), false)
3520         , _cursor (c)
3521         , _stop (s)
3522         , _grab_zoom (0.0)
3523 {
3524         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
3525 }
3526
3527 /** Do all the things we do when dragging the playhead to make it look as though
3528  *  we have located, without actually doing the locate (because that would cause
3529  *  the diskstream buffers to be refilled, which is too slow).
3530  */
3531 void
3532 CursorDrag::fake_locate (framepos_t t)
3533 {
3534         if (_editor->session () == 0) {
3535                 return;
3536         }
3537
3538         _editor->playhead_cursor->set_position (t);
3539
3540         Session* s = _editor->session ();
3541         if (s->timecode_transmission_suspended ()) {
3542                 framepos_t const f = _editor->playhead_cursor->current_frame ();
3543                 /* This is asynchronous so it will be sent "now"
3544                  */
3545                 s->send_mmc_locate (f);
3546                 /* These are synchronous and will be sent during the next
3547                    process cycle
3548                 */
3549                 s->queue_full_time_code ();
3550                 s->queue_song_position_pointer ();
3551         }
3552
3553         show_verbose_cursor_time (t);
3554         _editor->UpdateAllTransportClocks (t);
3555 }
3556
3557 void
3558 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
3559 {
3560         Drag::start_grab (event, c);
3561         setup_snap_delta (_editor->playhead_cursor->current_frame ());
3562
3563         _grab_zoom = _editor->samples_per_pixel;
3564
3565         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3566
3567         _editor->snap_to_with_modifier (where, event);
3568
3569         _editor->_dragging_playhead = true;
3570
3571         Session* s = _editor->session ();
3572
3573         /* grab the track canvas item as well */
3574
3575         _cursor.track_canvas_item().grab();
3576
3577         if (s) {
3578                 if (_was_rolling && _stop) {
3579                         s->request_stop ();
3580                 }
3581
3582                 if (s->is_auditioning()) {
3583                         s->cancel_audition ();
3584                 }
3585
3586
3587                 if (AudioEngine::instance()->connected()) {
3588
3589                         /* do this only if we're the engine is connected
3590                          * because otherwise this request will never be
3591                          * serviced and we'll busy wait forever. likewise,
3592                          * notice if we are disconnected while waiting for the
3593                          * request to be serviced.
3594                          */
3595
3596                         s->request_suspend_timecode_transmission ();
3597                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
3598                                 /* twiddle our thumbs */
3599                         }
3600                 }
3601         }
3602
3603         fake_locate (where - snap_delta (event->button.state));
3604 }
3605
3606 void
3607 CursorDrag::motion (GdkEvent* event, bool)
3608 {
3609         framepos_t where = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3610         _editor->snap_to_with_modifier (where, event);
3611         if (where != last_pointer_frame()) {
3612                 fake_locate (where - snap_delta (event->button.state));
3613         }
3614 }
3615
3616 void
3617 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
3618 {
3619         _editor->_dragging_playhead = false;
3620
3621         _cursor.track_canvas_item().ungrab();
3622
3623         if (!movement_occurred && _stop) {
3624                 return;
3625         }
3626
3627         motion (event, false);
3628
3629         Session* s = _editor->session ();
3630         if (s) {
3631                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
3632                 _editor->_pending_locate_request = true;
3633                 s->request_resume_timecode_transmission ();
3634         }
3635 }
3636
3637 void
3638 CursorDrag::aborted (bool)
3639 {
3640         _cursor.track_canvas_item().ungrab();
3641
3642         if (_editor->_dragging_playhead) {
3643                 _editor->session()->request_resume_timecode_transmission ();
3644                 _editor->_dragging_playhead = false;
3645         }
3646
3647         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
3648 }
3649
3650 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3651         : RegionDrag (e, i, p, v)
3652 {
3653         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
3654 }
3655
3656 void
3657 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3658 {
3659         Drag::start_grab (event, cursor);
3660
3661         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3662         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3663         setup_snap_delta (r->position ());
3664
3665         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
3666 }
3667
3668 void
3669 FadeInDrag::setup_pointer_frame_offset ()
3670 {
3671         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3672         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
3673         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
3674 }
3675
3676 void
3677 FadeInDrag::motion (GdkEvent* event, bool)
3678 {
3679         framecnt_t fade_length;
3680
3681         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3682         _editor->snap_to_with_modifier (pos, event);
3683         pos -= snap_delta (event->button.state);
3684
3685         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3686
3687         if (pos < (region->position() + 64)) {
3688                 fade_length = 64; // this should be a minimum defined somewhere
3689         } else if (pos > region->position() + region->length() - region->fade_out()->back()->when) {
3690                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3691         } else {
3692                 fade_length = pos - region->position();
3693         }
3694
3695         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3696
3697                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3698
3699                 if (!tmp) {
3700                         continue;
3701                 }
3702
3703                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
3704         }
3705
3706         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
3707 }
3708
3709 void
3710 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
3711 {
3712         if (!movement_occurred) {
3713                 return;
3714         }
3715
3716         framecnt_t fade_length;
3717         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3718         _editor->snap_to_with_modifier (pos, event);
3719         pos -= snap_delta (event->button.state);
3720
3721         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3722
3723         if (pos < (region->position() + 64)) {
3724                 fade_length = 64; // this should be a minimum defined somewhere
3725         } else if (pos >= region->position() + region->length() - region->fade_out()->back()->when) {
3726                 fade_length = region->length() - region->fade_out()->back()->when - 1;
3727         } else {
3728                 fade_length = pos - region->position();
3729         }
3730
3731         bool in_command = false;
3732
3733         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3734
3735                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3736
3737                 if (!tmp) {
3738                         continue;
3739                 }
3740
3741                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3742                 XMLNode &before = alist->get_state();
3743
3744                 tmp->audio_region()->set_fade_in_length (fade_length);
3745                 tmp->audio_region()->set_fade_in_active (true);
3746
3747                 if (!in_command) {
3748                         _editor->begin_reversible_command (_("change fade in length"));
3749                         in_command = true;
3750                 }
3751                 XMLNode &after = alist->get_state();
3752                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3753         }
3754
3755         if (in_command) {
3756                 _editor->commit_reversible_command ();
3757         }
3758 }
3759
3760 void
3761 FadeInDrag::aborted (bool)
3762 {
3763         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3764                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3765
3766                 if (!tmp) {
3767                         continue;
3768                 }
3769
3770                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3771         }
3772 }
3773
3774 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3775         : RegionDrag (e, i, p, v)
3776 {
3777         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3778 }
3779
3780 void
3781 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3782 {
3783         Drag::start_grab (event, cursor);
3784
3785         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3786         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3787         setup_snap_delta (r->last_frame ());
3788
3789         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3790 }
3791
3792 void
3793 FadeOutDrag::setup_pointer_frame_offset ()
3794 {
3795         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3796         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3797         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3798 }
3799
3800 void
3801 FadeOutDrag::motion (GdkEvent* event, bool)
3802 {
3803         framecnt_t fade_length;
3804
3805         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3806         _editor->snap_to_with_modifier (pos, event);
3807         pos -= snap_delta (event->button.state);
3808
3809         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3810
3811         if (pos > (region->last_frame() - 64)) {
3812                 fade_length = 64; // this should really be a minimum fade defined somewhere
3813         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3814                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3815         } else {
3816                 fade_length = region->last_frame() - pos;
3817         }
3818
3819         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3820
3821                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3822
3823                 if (!tmp) {
3824                         continue;
3825                 }
3826
3827                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3828         }
3829
3830         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3831 }
3832
3833 void
3834 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3835 {
3836         if (!movement_occurred) {
3837                 return;
3838         }
3839
3840         framecnt_t fade_length;
3841
3842         framepos_t pos = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
3843         _editor->snap_to_with_modifier (pos, event);
3844         pos -= snap_delta (event->button.state);
3845
3846         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (_primary->region ());
3847
3848         if (pos > (region->last_frame() - 64)) {
3849                 fade_length = 64; // this should really be a minimum fade defined somewhere
3850         } else if (pos <= region->position() + region->fade_in()->back()->when) {
3851                 fade_length = region->length() - region->fade_in()->back()->when - 1;
3852         } else {
3853                 fade_length = region->last_frame() - pos;
3854         }
3855
3856         bool in_command = false;
3857
3858         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3859
3860                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3861
3862                 if (!tmp) {
3863                         continue;
3864                 }
3865
3866                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3867                 XMLNode &before = alist->get_state();
3868
3869                 tmp->audio_region()->set_fade_out_length (fade_length);
3870                 tmp->audio_region()->set_fade_out_active (true);
3871
3872                 if (!in_command) {
3873                         _editor->begin_reversible_command (_("change fade out length"));
3874                         in_command = true;
3875                 }
3876                 XMLNode &after = alist->get_state();
3877                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3878         }
3879
3880         if (in_command) {
3881                 _editor->commit_reversible_command ();
3882         }
3883 }
3884
3885 void
3886 FadeOutDrag::aborted (bool)
3887 {
3888         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3889                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3890
3891                 if (!tmp) {
3892                         continue;
3893                 }
3894
3895                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3896         }
3897 }
3898
3899 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3900         : Drag (e, i)
3901         , _selection_changed (false)
3902 {
3903         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3904         Gtk::Window* toplevel = _editor->current_toplevel();
3905         _marker = reinterpret_cast<ArdourMarker*> (_item->get_data ("marker"));
3906
3907         assert (_marker);
3908
3909         _points.push_back (ArdourCanvas::Duple (0, 0));
3910
3911         _points.push_back (ArdourCanvas::Duple (0, toplevel ? physical_screen_height (toplevel->get_window()) : 900));
3912 }
3913
3914 MarkerDrag::~MarkerDrag ()
3915 {
3916         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3917                 delete i->location;
3918         }
3919 }
3920
3921 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, ArdourMarker* m)
3922 {
3923         location = new Location (*l);
3924         markers.push_back (m);
3925         move_both = false;
3926 }
3927
3928 void
3929 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3930 {
3931         Drag::start_grab (event, cursor);
3932
3933         bool is_start;
3934
3935         Location *location = _editor->find_location_from_marker (_marker, is_start);
3936         _editor->_dragging_edit_point = true;
3937
3938         update_item (location);
3939
3940         // _drag_line->show();
3941         // _line->raise_to_top();
3942
3943         if (is_start) {
3944                 show_verbose_cursor_time (location->start());
3945         } else {
3946                 show_verbose_cursor_time (location->end());
3947         }
3948         setup_snap_delta (is_start ? location->start() : location->end());
3949
3950         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3951
3952         switch (op) {
3953         case Selection::Toggle:
3954                 /* we toggle on the button release */
3955                 break;
3956         case Selection::Set:
3957                 if (!_editor->selection->selected (_marker)) {
3958                         _editor->selection->set (_marker);
3959                         _selection_changed = true;
3960                 }
3961                 break;
3962         case Selection::Extend:
3963         {
3964                 Locations::LocationList ll;
3965                 list<ArdourMarker*> to_add;
3966                 framepos_t s, e;
3967                 _editor->selection->markers.range (s, e);
3968                 s = min (_marker->position(), s);
3969                 e = max (_marker->position(), e);
3970                 s = min (s, e);
3971                 e = max (s, e);
3972                 if (e < max_framepos) {
3973                         ++e;
3974                 }
3975                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3976                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3977                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3978                         if (lm) {
3979                                 if (lm->start) {
3980                                         to_add.push_back (lm->start);
3981                                 }
3982                                 if (lm->end) {
3983                                         to_add.push_back (lm->end);
3984                                 }
3985                         }
3986                 }
3987                 if (!to_add.empty()) {
3988                         _editor->selection->add (to_add);
3989                         _selection_changed = true;
3990                 }
3991                 break;
3992         }
3993         case Selection::Add:
3994                 _editor->selection->add (_marker);
3995                 _selection_changed = true;
3996
3997                 break;
3998         }
3999
4000         /* Set up copies for us to manipulate during the drag
4001          */
4002
4003         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
4004
4005                 Location* l = _editor->find_location_from_marker (*i, is_start);
4006
4007                 if (!l) {
4008                         continue;
4009                 }
4010
4011                 if (l->is_mark()) {
4012                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4013                 } else {
4014                         /* range: check that the other end of the range isn't
4015                            already there.
4016                         */
4017                         CopiedLocationInfo::iterator x;
4018                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4019                                 if (*(*x).location == *l) {
4020                                         break;
4021                                 }
4022                         }
4023                         if (x == _copied_locations.end()) {
4024                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
4025                         } else {
4026                                 (*x).markers.push_back (*i);
4027                                 (*x).move_both = true;
4028                         }
4029                 }
4030
4031         }
4032 }
4033
4034 void
4035 MarkerDrag::setup_pointer_frame_offset ()
4036 {
4037         bool is_start;
4038         Location *location = _editor->find_location_from_marker (_marker, is_start);
4039         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
4040 }
4041
4042 void
4043 MarkerDrag::motion (GdkEvent* event, bool)
4044 {
4045         framecnt_t f_delta = 0;
4046         bool is_start;
4047         bool move_both = false;
4048         Location *real_location;
4049         Location *copy_location = 0;
4050         framecnt_t const sd = snap_delta (event->button.state);
4051
4052         framecnt_t const newframe = adjusted_frame (_drags->current_pointer_frame () + sd, event, true) - sd;
4053         framepos_t next = newframe;
4054
4055         if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::push_points_modifier ())) {
4056                 move_both = true;
4057         }
4058
4059         CopiedLocationInfo::iterator x;
4060
4061         /* find the marker we're dragging, and compute the delta */
4062
4063         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4064
4065                 copy_location = (*x).location;
4066
4067                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
4068
4069                         /* this marker is represented by this
4070                          * CopiedLocationMarkerInfo
4071                          */
4072
4073                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
4074                                 /* que pasa ?? */
4075                                 return;
4076                         }
4077
4078                         if (real_location->is_mark()) {
4079                                 f_delta = newframe - copy_location->start();
4080                         } else {
4081
4082
4083                                 switch (_marker->type()) {
4084                                 case ArdourMarker::SessionStart:
4085                                 case ArdourMarker::RangeStart:
4086                                 case ArdourMarker::LoopStart:
4087                                 case ArdourMarker::PunchIn:
4088                                         f_delta = newframe - copy_location->start();
4089                                         break;
4090
4091                                 case ArdourMarker::SessionEnd:
4092                                 case ArdourMarker::RangeEnd:
4093                                 case ArdourMarker::LoopEnd:
4094                                 case ArdourMarker::PunchOut:
4095                                         f_delta = newframe - copy_location->end();
4096                                         break;
4097                                 default:
4098                                         /* what kind of marker is this ? */
4099                                         return;
4100                                 }
4101                         }
4102
4103                         break;
4104                 }
4105         }
4106
4107         if (x == _copied_locations.end()) {
4108                 /* hmm, impossible - we didn't find the dragged marker */
4109                 return;
4110         }
4111
4112         /* now move them all */
4113
4114         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4115
4116                 copy_location = x->location;
4117
4118                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
4119                         continue;
4120                 }
4121
4122                 if (real_location->locked()) {
4123                         continue;
4124                 }
4125
4126                 if (copy_location->is_mark()) {
4127
4128                         /* now move it */
4129
4130                         copy_location->set_start (copy_location->start() + f_delta);
4131
4132                 } else {
4133
4134                         framepos_t new_start = copy_location->start() + f_delta;
4135                         framepos_t new_end = copy_location->end() + f_delta;
4136
4137                         if (is_start) { // start-of-range marker
4138
4139                                 if (move_both || (*x).move_both) {
4140                                         copy_location->set_start (new_start);
4141                                         copy_location->set_end (new_end);
4142                                 } else  if (new_start < copy_location->end()) {
4143                                         copy_location->set_start (new_start);
4144                                 } else if (newframe > 0) {
4145                                         //_editor->snap_to (next, RoundUpAlways, true);
4146                                         copy_location->set_end (next);
4147                                         copy_location->set_start (newframe);
4148                                 }
4149
4150                         } else { // end marker
4151
4152                                 if (move_both || (*x).move_both) {
4153                                         copy_location->set_end (new_end);
4154                                         copy_location->set_start (new_start);
4155                                 } else if (new_end > copy_location->start()) {
4156                                         copy_location->set_end (new_end);
4157                                 } else if (newframe > 0) {
4158                                         //_editor->snap_to (next, RoundDownAlways, true);
4159                                         copy_location->set_start (next);
4160                                         copy_location->set_end (newframe);
4161                                 }
4162                         }
4163                 }
4164
4165                 update_item (copy_location);
4166
4167                 /* now lookup the actual GUI items used to display this
4168                  * location and move them to wherever the copy of the location
4169                  * is now. This means that the logic in ARDOUR::Location is
4170                  * still enforced, even though we are not (yet) modifying
4171                  * the real Location itself.
4172                  */
4173
4174                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
4175
4176                 if (lm) {
4177                         lm->set_position (copy_location->start(), copy_location->end());
4178                 }
4179
4180         }
4181
4182         assert (!_copied_locations.empty());
4183
4184         show_verbose_cursor_time (newframe);
4185 }
4186
4187 void
4188 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
4189 {
4190         if (!movement_occurred) {
4191
4192                 if (was_double_click()) {
4193                         _editor->rename_marker (_marker);
4194                         return;
4195                 }
4196
4197                 /* just a click, do nothing but finish
4198                    off the selection process
4199                 */
4200
4201                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
4202                 switch (op) {
4203                 case Selection::Set:
4204                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
4205                                 _editor->selection->set (_marker);
4206                                 _selection_changed = true;
4207                         }
4208                         break;
4209
4210                 case Selection::Toggle:
4211                         /* we toggle on the button release, click only */
4212                         _editor->selection->toggle (_marker);
4213                         _selection_changed = true;
4214
4215                         break;
4216
4217                 case Selection::Extend:
4218                 case Selection::Add:
4219                         break;
4220                 }
4221
4222                 if (_selection_changed) {
4223                         _editor->begin_reversible_selection_op(X_("Select Marker Release"));
4224                         _editor->commit_reversible_selection_op();
4225                 }
4226
4227                 return;
4228         }
4229
4230         _editor->_dragging_edit_point = false;
4231
4232         XMLNode &before = _editor->session()->locations()->get_state();
4233         bool in_command = false;
4234
4235         MarkerSelection::iterator i;
4236         CopiedLocationInfo::iterator x;
4237         bool is_start;
4238
4239         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
4240              x != _copied_locations.end() && i != _editor->selection->markers.end();
4241              ++i, ++x) {
4242
4243                 Location * location = _editor->find_location_from_marker (*i, is_start);
4244
4245                 if (location) {
4246
4247                         if (location->locked()) {
4248                                 continue;
4249                         }
4250                         if (!in_command) {
4251                                 _editor->begin_reversible_command ( _("move marker") );
4252                                 in_command = true;
4253                         }
4254                         if (location->is_mark()) {
4255                                 location->set_start (((*x).location)->start());
4256                         } else {
4257                                 location->set (((*x).location)->start(), ((*x).location)->end());
4258                         }
4259
4260                         if (location->is_session_range()) {
4261                                 _editor->session()->set_end_is_free (false);
4262                         }
4263                 }
4264         }
4265
4266         if (in_command) {
4267                 XMLNode &after = _editor->session()->locations()->get_state();
4268                 _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4269                 _editor->commit_reversible_command ();
4270         }
4271 }
4272
4273 void
4274 MarkerDrag::aborted (bool movement_occurred)
4275 {
4276         if (!movement_occurred) {
4277                 return;
4278         }
4279
4280         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
4281
4282                 /* move all markers to their original location */
4283
4284
4285                 for (vector<ArdourMarker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
4286
4287                         bool is_start;
4288                         Location * location = _editor->find_location_from_marker (*m, is_start);
4289
4290                         if (location) {
4291                                 (*m)->set_position (is_start ? location->start() : location->end());
4292                         }
4293                 }
4294         }
4295 }
4296
4297 void
4298 MarkerDrag::update_item (Location*)
4299 {
4300         /* noop */
4301 }
4302
4303 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
4304         : Drag (e, i)
4305         , _fixed_grab_x (0.0)
4306         , _fixed_grab_y (0.0)
4307         , _cumulative_x_drag (0.0)
4308         , _cumulative_y_drag (0.0)
4309         , _pushing (false)
4310         , _final_index (0)
4311 {
4312         if (_zero_gain_fraction < 0.0) {
4313                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
4314         }
4315
4316         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
4317
4318         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
4319         assert (_point);
4320 }
4321
4322
4323 void
4324 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4325 {
4326         Drag::start_grab (event, _editor->cursors()->fader);
4327
4328         // start the grab at the center of the control point so
4329         // the point doesn't 'jump' to the mouse after the first drag
4330         _fixed_grab_x = _point->get_x();
4331         _fixed_grab_y = _point->get_y();
4332
4333         framepos_t pos = _editor->pixel_to_sample (_fixed_grab_x);
4334         setup_snap_delta (pos);
4335
4336         float const fraction = 1 - (_point->get_y() / _point->line().height());
4337         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
4338
4339         _pushing = Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::push_points_modifier ());
4340
4341         if (!_point->can_slide ()) {
4342                 _x_constrained = true;
4343         }
4344 }
4345
4346 void
4347 ControlPointDrag::motion (GdkEvent* event, bool first_motion)
4348 {
4349         double dx = _drags->current_pointer_x() - last_pointer_x();
4350         double dy = current_pointer_y() - last_pointer_y();
4351         bool need_snap = true;
4352
4353         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4354                 dx *= 0.1;
4355                 dy *= 0.1;
4356                 need_snap = false;
4357         }
4358
4359         /* coordinate in pixels relative to the start of the region (for region-based automation)
4360            or track (for track-based automation) */
4361         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
4362         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4363
4364         // calculate zero crossing point. back off by .01 to stay on the
4365         // positive side of zero
4366         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
4367
4368         if (_x_constrained) {
4369                 cx = _fixed_grab_x;
4370         }
4371         if (_y_constrained) {
4372                 cy = _fixed_grab_y;
4373         }
4374
4375         _cumulative_x_drag = cx - _fixed_grab_x;
4376         _cumulative_y_drag = cy - _fixed_grab_y;
4377
4378         cx = max (0.0, cx);
4379         cy = max (0.0, cy);
4380         cy = min ((double) _point->line().height(), cy);
4381
4382         // make sure we hit zero when passing through
4383         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
4384                 cy = zero_gain_y;
4385         }
4386
4387         framepos_t cx_frames = _editor->pixel_to_sample (cx) + snap_delta (event->button.state);
4388
4389         if (!_x_constrained && need_snap) {
4390                 _editor->snap_to_with_modifier (cx_frames, event);
4391         }
4392
4393         cx_frames -= snap_delta (event->button.state);
4394         cx_frames = min (cx_frames, _point->line().maximum_time());
4395
4396         float const fraction = 1.0 - (cy / _point->line().height());
4397
4398         if (first_motion) {
4399                 float const initial_fraction = 1.0 - (_fixed_grab_y / _point->line().height());
4400                 _editor->begin_reversible_command (_("automation event move"));
4401                 _point->line().start_drag_single (_point, _fixed_grab_x, initial_fraction);
4402         }
4403         pair<double, float> result;
4404         result = _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
4405
4406         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (result.second));
4407 }
4408
4409 void
4410 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
4411 {
4412         if (!movement_occurred) {
4413
4414                 /* just a click */
4415                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier))) {
4416                         _editor->reset_point_selection ();
4417                 }
4418
4419         } else {
4420                 _point->line().end_drag (_pushing, _final_index);
4421                 _editor->commit_reversible_command ();
4422         }
4423 }
4424
4425 void
4426 ControlPointDrag::aborted (bool)
4427 {
4428         _point->line().reset ();
4429 }
4430
4431 bool
4432 ControlPointDrag::active (Editing::MouseMode m)
4433 {
4434         if (m == Editing::MouseDraw) {
4435                 /* always active in mouse draw */
4436                 return true;
4437         }
4438
4439         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
4440         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
4441 }
4442
4443 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
4444         : Drag (e, i)
4445         , _line (0)
4446         , _fixed_grab_x (0.0)
4447         , _fixed_grab_y (0.0)
4448         , _cumulative_y_drag (0)
4449         , _before (0)
4450         , _after (0)
4451 {
4452         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
4453 }
4454
4455 void
4456 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4457 {
4458         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
4459         assert (_line);
4460
4461         _item = &_line->grab_item ();
4462
4463         /* need to get x coordinate in terms of parent (TimeAxisItemView)
4464            origin, and ditto for y.
4465         */
4466
4467         double mx = event->button.x;
4468         double my = event->button.y;
4469
4470         _line->grab_item().canvas_to_item (mx, my);
4471
4472         framecnt_t const frame_within_region = (framecnt_t) floor (mx * _editor->samples_per_pixel);
4473
4474         if (!_line->control_points_adjacent (frame_within_region, _before, _after)) {
4475                 /* no adjacent points */
4476                 return;
4477         }
4478
4479         Drag::start_grab (event, _editor->cursors()->fader);
4480
4481         /* store grab start in item frame */
4482         double const bx = _line->nth (_before)->get_x();
4483         double const ax = _line->nth (_after)->get_x();
4484         double const click_ratio = (ax - mx) / (ax - bx);
4485
4486         double const cy = ((_line->nth (_before)->get_y() * click_ratio) + (_line->nth (_after)->get_y() * (1 - click_ratio)));
4487
4488         _fixed_grab_x = mx;
4489         _fixed_grab_y = cy;
4490
4491         double fraction = 1.0 - (cy / _line->height());
4492
4493         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
4494 }
4495
4496 void
4497 LineDrag::motion (GdkEvent* event, bool first_move)
4498 {
4499         double dy = current_pointer_y() - last_pointer_y();
4500
4501         if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::fine_adjust_modifier ())) {
4502                 dy *= 0.1;
4503         }
4504
4505         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
4506
4507         _cumulative_y_drag = cy - _fixed_grab_y;
4508
4509         cy = max (0.0, cy);
4510         cy = min ((double) _line->height(), cy);
4511
4512         double const fraction = 1.0 - (cy / _line->height());
4513         uint32_t ignored;
4514
4515         if (first_move) {
4516                 float const initial_fraction = 1.0 - (_fixed_grab_y / _line->height());
4517
4518                 _editor->begin_reversible_command (_("automation range move"));
4519                 _line->start_drag_line (_before, _after, initial_fraction);
4520         }
4521
4522         /* we are ignoring x position for this drag, so we can just pass in anything */
4523         pair<double, float> result;
4524
4525         result = _line->drag_motion (0, fraction, true, false, ignored);
4526         show_verbose_cursor_text (_line->get_verbose_cursor_string (result.second));
4527 }
4528
4529 void
4530 LineDrag::finished (GdkEvent* event, bool movement_occurred)
4531 {
4532         if (movement_occurred) {
4533                 motion (event, false);
4534                 _line->end_drag (false, 0);
4535                 _editor->commit_reversible_command ();
4536         } else {
4537                 /* add a new control point on the line */
4538
4539                 AutomationTimeAxisView* atv;
4540
4541                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4542                         framepos_t where = grab_frame ();
4543
4544                         double cx = 0;
4545                         double cy = _fixed_grab_y;
4546
4547                         _line->grab_item().item_to_canvas (cx, cy);
4548
4549                         atv->add_automation_event (event, where, cy, false);
4550                 } else if (dynamic_cast<AudioTimeAxisView*>(_editor->clicked_axisview) != 0) {
4551                         AudioRegionView* arv;
4552
4553                         if ((arv = dynamic_cast<AudioRegionView*>(_editor->clicked_regionview)) != 0) {
4554                                 arv->add_gain_point_event (&arv->get_gain_line()->grab_item(), event, false);
4555                         }
4556                 }
4557         }
4558 }
4559
4560 void
4561 LineDrag::aborted (bool)
4562 {
4563         _line->reset ();
4564 }
4565
4566 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
4567         : Drag (e, i),
4568           _line (0),
4569           _arv (0),
4570           _region_view_grab_x (0.0),
4571           _cumulative_x_drag (0),
4572           _before (0.0),
4573           _max_x (0)
4574 {
4575         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
4576 }
4577
4578 void
4579 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
4580 {
4581         Drag::start_grab (event);
4582
4583         _line = reinterpret_cast<Line*> (_item);
4584         assert (_line);
4585
4586         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
4587
4588         double cx = event->button.x;
4589         double cy = event->button.y;
4590
4591         _item->parent()->canvas_to_item (cx, cy);
4592
4593         /* store grab start in parent frame */
4594         _region_view_grab_x = cx;
4595
4596         _before = *(float*) _item->get_data ("position");
4597
4598         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4599
4600         _max_x = _editor->sample_to_pixel(_arv->get_duration());
4601 }
4602
4603 void
4604 FeatureLineDrag::motion (GdkEvent*, bool)
4605 {
4606         double dx = _drags->current_pointer_x() - last_pointer_x();
4607
4608         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
4609
4610         _cumulative_x_drag += dx;
4611
4612         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
4613
4614         if (cx > _max_x){
4615                 cx = _max_x;
4616         }
4617         else if(cx < 0){
4618                 cx = 0;
4619         }
4620
4621         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
4622         assert (bbox);
4623         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
4624
4625         float *pos = new float;
4626         *pos = cx;
4627
4628         _line->set_data ("position", pos);
4629
4630         _before = cx;
4631 }
4632
4633 void
4634 FeatureLineDrag::finished (GdkEvent*, bool)
4635 {
4636         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
4637         _arv->update_transient(_before, _before);
4638 }
4639
4640 void
4641 FeatureLineDrag::aborted (bool)
4642 {
4643         //_line->reset ();
4644 }
4645
4646 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4647         : Drag (e, i)
4648         , _vertical_only (false)
4649 {
4650         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
4651 }
4652
4653 void
4654 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4655 {
4656         Drag::start_grab (event);
4657         show_verbose_cursor_time (adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid()));
4658 }
4659
4660 void
4661 RubberbandSelectDrag::motion (GdkEvent* event, bool)
4662 {
4663         framepos_t start;
4664         framepos_t end;
4665         double y1;
4666         double y2;
4667
4668         framepos_t const pf = adjusted_current_frame (event, UIConfiguration::instance().get_rubberbanding_snaps_to_grid());
4669
4670         framepos_t grab = grab_frame ();
4671         if (UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4672                 _editor->snap_to_with_modifier (grab, event);
4673         } else {
4674                 grab = raw_grab_frame ();
4675         }
4676
4677         /* base start and end on initial click position */
4678
4679         if (pf < grab) {
4680                 start = pf;
4681                 end = grab;
4682         } else {
4683                 end = pf;
4684                 start = grab;
4685         }
4686
4687         if (current_pointer_y() < grab_y()) {
4688                 y1 = current_pointer_y();
4689                 y2 = grab_y();
4690         } else {
4691                 y2 = current_pointer_y();
4692                 y1 = grab_y();
4693         }
4694
4695         if (start != end || y1 != y2) {
4696
4697                 double x1 = _editor->sample_to_pixel (start);
4698                 double x2 = _editor->sample_to_pixel (end);
4699                 const double min_dimension = 2.0;
4700
4701                 if (_vertical_only) {
4702                         /* fixed 10 pixel width */
4703                         x2 = x1 + 10;
4704                 } else {
4705                         if (x2 < x1) {
4706                                 x2 = min (x1 - min_dimension, x2);
4707                         } else {
4708                                 x2 = max (x1 + min_dimension, x2);
4709                         }
4710                 }
4711
4712                 if (y2 < y1) {
4713                         y2 = min (y1 - min_dimension, y2);
4714                 } else {
4715                         y2 = max (y1 + min_dimension, y2);
4716                 }
4717
4718                 /* translate rect into item space and set */
4719
4720                 ArdourCanvas::Rect r (x1, y1, x2, y2);
4721
4722                 /* this drag is a _trackview_only == true drag, so the y1 and
4723                  * y2 (computed using current_pointer_y() and grab_y()) will be
4724                  * relative to the top of the trackview group). The
4725                  * rubberband rect has the same parent/scroll offset as the
4726                  * the trackview group, so we can use the "r" rect directly
4727                  * to set the shape of the rubberband.
4728                  */
4729
4730                 _editor->rubberband_rect->set (r);
4731                 _editor->rubberband_rect->show();
4732                 _editor->rubberband_rect->raise_to_top();
4733
4734                 show_verbose_cursor_time (pf);
4735
4736                 do_select_things (event, true);
4737         }
4738 }
4739
4740 void
4741 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
4742 {
4743         framepos_t x1;
4744         framepos_t x2;
4745         framepos_t grab = grab_frame ();
4746         framepos_t lpf = last_pointer_frame ();
4747
4748         if (!UIConfiguration::instance().get_rubberbanding_snaps_to_grid ()) {
4749                 grab = raw_grab_frame ();
4750                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
4751         }
4752
4753         if (grab < lpf) {
4754                 x1 = grab;
4755                 x2 = lpf;
4756         } else {
4757                 x2 = grab;
4758                 x1 = lpf;
4759         }
4760
4761         double y1;
4762         double y2;
4763
4764         if (current_pointer_y() < grab_y()) {
4765                 y1 = current_pointer_y();
4766                 y2 = grab_y();
4767         } else {
4768                 y2 = current_pointer_y();
4769                 y1 = grab_y();
4770         }
4771
4772         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4773 }
4774
4775 void
4776 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4777 {
4778         if (movement_occurred) {
4779
4780                 motion (event, false);
4781                 do_select_things (event, false);
4782
4783         } else {
4784
4785                 /* just a click */
4786
4787                 bool do_deselect = true;
4788                 MidiTimeAxisView* mtv;
4789
4790                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4791                         /* MIDI track */
4792                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4793                                 /* nothing selected */
4794                                 add_midi_region (mtv, true, _editor->get_grid_music_divisions(event->button.state));
4795                                 do_deselect = false;
4796                         }
4797                 }
4798
4799                 /* do not deselect if Primary or Tertiary (toggle-select or
4800                  * extend-select are pressed.
4801                  */
4802
4803                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) &&
4804                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) &&
4805                     do_deselect) {
4806                         deselect_things ();
4807                 }
4808
4809         }
4810
4811         _editor->rubberband_rect->hide();
4812 }
4813
4814 void
4815 RubberbandSelectDrag::aborted (bool)
4816 {
4817         _editor->rubberband_rect->hide ();
4818 }
4819
4820 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4821         : RegionDrag (e, i, p, v)
4822 {
4823         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4824 }
4825
4826 void
4827 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4828 {
4829         Drag::start_grab (event, cursor);
4830
4831         _editor->get_selection().add (_primary);
4832
4833         framepos_t where = _primary->region()->position();
4834         setup_snap_delta (where);
4835
4836         show_verbose_cursor_duration (where, adjusted_current_frame (event), 0);
4837 }
4838
4839 void
4840 TimeFXDrag::motion (GdkEvent* event, bool)
4841 {
4842         RegionView* rv = _primary;
4843         StreamView* cv = rv->get_time_axis_view().view ();
4844
4845         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4846         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4847         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4848         framepos_t pf = _editor->canvas_event_sample (event) + snap_delta (event->button.state);
4849         _editor->snap_to_with_modifier (pf, event);
4850         pf -= snap_delta (event->button.state);
4851
4852         if (pf > rv->region()->position()) {
4853                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4854         }
4855
4856         show_verbose_cursor_duration (_primary->region()->position(), pf, 0);
4857 }
4858
4859 void
4860 TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
4861 {
4862         /* this may have been a single click, no drag. We still want the dialog
4863            to show up in that case, so that the user can manually edit the
4864            parameters for the timestretch.
4865         */
4866
4867         float fraction = 1.0;
4868
4869         if (movement_occurred) {
4870
4871                 motion (event, false);
4872
4873                 _primary->get_time_axis_view().hide_timestretch ();
4874
4875                 framepos_t adjusted_frame_pos = adjusted_current_frame (event);
4876
4877                 if (adjusted_frame_pos < _primary->region()->position()) {
4878                         /* backwards drag of the left edge - not usable */
4879                         return;
4880                 }
4881
4882                 framecnt_t newlen = adjusted_frame_pos - _primary->region()->position();
4883
4884                 fraction = (double) newlen / (double) _primary->region()->length();
4885
4886 #ifndef USE_RUBBERBAND
4887                 // Soundtouch uses fraction / 100 instead of normal (/ 1)
4888                 if (_primary->region()->data_type() == DataType::AUDIO) {
4889                         fraction = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4890                 }
4891 #endif
4892         }
4893
4894         if (!_editor->get_selection().regions.empty()) {
4895                 /* primary will already be included in the selection, and edit
4896                    group shared editing will propagate selection across
4897                    equivalent regions, so just use the current region
4898                    selection.
4899                 */
4900
4901                 if (_editor->time_stretch (_editor->get_selection().regions, fraction) == -1) {
4902                         error << _("An error occurred while executing time stretch operation") << endmsg;
4903                 }
4904         }
4905 }
4906
4907 void
4908 TimeFXDrag::aborted (bool)
4909 {
4910         _primary->get_time_axis_view().hide_timestretch ();
4911 }
4912
4913 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4914         : Drag (e, i)
4915 {
4916         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4917 }
4918
4919 void
4920 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4921 {
4922         Drag::start_grab (event);
4923 }
4924
4925 void
4926 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4927 {
4928         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4929 }
4930
4931 void
4932 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4933 {
4934         if (movement_occurred && _editor->session()) {
4935                 /* make sure we stop */
4936                 _editor->session()->request_transport_speed (0.0);
4937         }
4938 }
4939
4940 void
4941 ScrubDrag::aborted (bool)
4942 {
4943         /* XXX: TODO */
4944 }
4945
4946 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4947         : Drag (e, i)
4948         , _operation (o)
4949         , _add (false)
4950         , _time_selection_at_start (!_editor->get_selection().time.empty())
4951 {
4952         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4953
4954         if (_time_selection_at_start) {
4955                 start_at_start = _editor->get_selection().time.start();
4956                 end_at_start = _editor->get_selection().time.end_frame();
4957         }
4958 }
4959
4960 void
4961 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4962 {
4963         if (_editor->session() == 0) {
4964                 return;
4965         }
4966
4967         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4968
4969         switch (_operation) {
4970         case CreateSelection:
4971                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4972                         _add = true;
4973                 } else {
4974                         _add = false;
4975                 }
4976                 cursor = _editor->cursors()->selector;
4977                 Drag::start_grab (event, cursor);
4978                 break;
4979
4980         case SelectionStartTrim:
4981                 if (_editor->clicked_axisview) {
4982                         _editor->clicked_axisview->order_selection_trims (_item, true);
4983                 }
4984                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4985                 break;
4986
4987         case SelectionEndTrim:
4988                 if (_editor->clicked_axisview) {
4989                         _editor->clicked_axisview->order_selection_trims (_item, false);
4990                 }
4991                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4992                 break;
4993
4994         case SelectionMove:
4995                 Drag::start_grab (event, cursor);
4996                 break;
4997
4998         case SelectionExtend:
4999                 Drag::start_grab (event, cursor);
5000                 break;
5001         }
5002
5003         if (_operation == SelectionMove) {
5004                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
5005         } else {
5006                 show_verbose_cursor_time (adjusted_current_frame (event));
5007         }
5008 }
5009
5010 void
5011 SelectionDrag::setup_pointer_frame_offset ()
5012 {
5013         switch (_operation) {
5014         case CreateSelection:
5015                 _pointer_frame_offset = 0;
5016                 break;
5017
5018         case SelectionStartTrim:
5019         case SelectionMove:
5020                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
5021                 break;
5022
5023         case SelectionEndTrim:
5024                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
5025                 break;
5026
5027         case SelectionExtend:
5028                 break;
5029         }
5030 }
5031
5032 void
5033 SelectionDrag::motion (GdkEvent* event, bool first_move)
5034 {
5035         framepos_t start = 0;
5036         framepos_t end = 0;
5037         framecnt_t length = 0;
5038         framecnt_t distance = 0;
5039
5040         framepos_t const pending_position = adjusted_current_frame (event);
5041
5042         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
5043                 return;
5044         }
5045
5046         switch (_operation) {
5047         case CreateSelection:
5048         {
5049                 framepos_t grab = grab_frame ();
5050
5051                 if (first_move) {
5052                         grab = adjusted_current_frame (event, false);
5053                         if (grab < pending_position) {
5054                                 _editor->snap_to (grab, RoundDownMaybe);
5055                         }  else {
5056                                 _editor->snap_to (grab, RoundUpMaybe);
5057                         }
5058                 }
5059
5060                 if (pending_position < grab) {
5061                         start = pending_position;
5062                         end = grab;
5063                 } else {
5064                         end = pending_position;
5065                         start = grab;
5066                 }
5067
5068                 /* first drag: Either add to the selection
5069                    or create a new selection
5070                 */
5071
5072                 if (first_move) {
5073
5074                         if (_add) {
5075
5076                                 /* adding to the selection */
5077                                 _editor->set_selected_track_as_side_effect (Selection::Add);
5078                                 _editor->clicked_selection = _editor->selection->add (start, end);
5079                                 _add = false;
5080
5081                         } else {
5082
5083                                 /* new selection */
5084
5085                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5086                                         _editor->set_selected_track_as_side_effect (Selection::Set);
5087                                 }
5088
5089                                 _editor->clicked_selection = _editor->selection->set (start, end);
5090                         }
5091                 }
5092
5093                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff,
5094                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this
5095                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
5096                 if (atest) {
5097                         _editor->selection->add (atest);
5098                         break;
5099                 }
5100
5101                 /* select all tracks within the rectangle that we've marked out so far */
5102                 TrackViewList new_selection;
5103                 TrackViewList& all_tracks (_editor->track_views);
5104
5105                 ArdourCanvas::Coord const top = grab_y();
5106                 ArdourCanvas::Coord const bottom = current_pointer_y();
5107
5108                 if (top >= 0 && bottom >= 0) {
5109
5110                         //first, find the tracks that are covered in the y range selection
5111                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
5112                                 if ((*i)->covered_by_y_range (top, bottom)) {
5113                                         new_selection.push_back (*i);
5114                                 }
5115                         }
5116
5117                         //now find any tracks that are GROUPED with the tracks we selected
5118                         TrackViewList grouped_add = new_selection;
5119                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
5120                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
5121                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::group_select.property_id) ) {
5122                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
5123                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
5124                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
5125                                                         grouped_add.push_back (*j);
5126                                         }
5127                                 }
5128                         }
5129
5130                         //now compare our list with the current selection, and add or remove as necessary
5131                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
5132                         TrackViewList tracks_to_add;
5133                         TrackViewList tracks_to_remove;
5134                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
5135                                 if ( !_editor->selection->tracks.contains ( *i ) )
5136                                         tracks_to_add.push_back ( *i );
5137                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
5138                                 if ( !grouped_add.contains ( *i ) )
5139                                         tracks_to_remove.push_back ( *i );
5140                         _editor->selection->add(tracks_to_add);
5141                         _editor->selection->remove(tracks_to_remove);
5142
5143                 }
5144         }
5145         break;
5146
5147         case SelectionStartTrim:
5148
5149                 end = _editor->selection->time[_editor->clicked_selection].end;
5150
5151                 if (pending_position > end) {
5152                         start = end;
5153                 } else {
5154                         start = pending_position;
5155                 }
5156                 break;
5157
5158         case SelectionEndTrim:
5159
5160                 start = _editor->selection->time[_editor->clicked_selection].start;
5161
5162                 if (pending_position < start) {
5163                         end = start;
5164                 } else {
5165                         end = pending_position;
5166                 }
5167
5168                 break;
5169
5170         case SelectionMove:
5171
5172                 start = _editor->selection->time[_editor->clicked_selection].start;
5173                 end = _editor->selection->time[_editor->clicked_selection].end;
5174
5175                 length = end - start;
5176                 distance = pending_position - start;
5177                 start = pending_position;
5178                 _editor->snap_to (start);
5179
5180                 end = start + length;
5181
5182                 break;
5183
5184         case SelectionExtend:
5185                 break;
5186         }
5187
5188         if (start != end) {
5189                 switch (_operation) {
5190                 case SelectionMove:
5191                         if (_time_selection_at_start) {
5192                                 _editor->selection->move_time (distance);
5193                         }
5194                         break;
5195                 default:
5196                         _editor->selection->replace (_editor->clicked_selection, start, end);
5197                 }
5198         }
5199
5200         if (_operation == SelectionMove) {
5201                 show_verbose_cursor_time(start);
5202         } else {
5203                 show_verbose_cursor_time(pending_position);
5204         }
5205 }
5206
5207 void
5208 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
5209 {
5210         Session* s = _editor->session();
5211
5212         _editor->begin_reversible_selection_op (X_("Change Time Selection"));
5213         if (movement_occurred) {
5214                 motion (event, false);
5215                 /* XXX this is not object-oriented programming at all. ick */
5216                 if (_editor->selection->time.consolidate()) {
5217                         _editor->selection->TimeChanged ();
5218                 }
5219
5220                 /* XXX what if its a music time selection? */
5221                 if (s) {
5222                         if (s->get_play_range() && s->transport_rolling()) {
5223                                 s->request_play_range (&_editor->selection->time, true);
5224                         } else if (!s->config.get_external_sync()) {
5225                                 if (UIConfiguration::instance().get_follow_edits() && !s->transport_rolling()) {
5226                                         if (_operation == SelectionEndTrim)
5227                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
5228                                         else
5229                                                 s->request_locate (_editor->get_selection().time.start());
5230                                 }
5231                         }
5232
5233                         if (_editor->get_selection().time.length() != 0) {
5234                                 s->set_range_selection (_editor->get_selection().time.start(), _editor->get_selection().time.end_frame());
5235                         } else {
5236                                 s->clear_range_selection ();
5237                         }
5238                 }
5239
5240         } else {
5241                 /* just a click, no pointer movement.
5242                  */
5243
5244                 if (_operation == SelectionExtend) {
5245                         if (_time_selection_at_start) {
5246                                 framepos_t pos = adjusted_current_frame (event, false);
5247                                 framepos_t start = min (pos, start_at_start);
5248                                 framepos_t end = max (pos, end_at_start);
5249                                 _editor->selection->set (start, end);
5250                         }
5251                 } else {
5252                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5253                                 if (_editor->clicked_selection) {
5254                                         _editor->selection->remove (_editor->clicked_selection);
5255                                 }
5256                         } else {
5257                                 if (!_editor->clicked_selection) {
5258                                         _editor->selection->clear_time();
5259                                 }
5260                         }
5261                 }
5262
5263                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
5264                         _editor->selection->set (_editor->clicked_axisview);
5265                 }
5266
5267                 if (s && s->get_play_range () && s->transport_rolling()) {
5268                         s->request_stop (false, false);
5269                 }
5270
5271         }
5272
5273         _editor->stop_canvas_autoscroll ();
5274         _editor->clicked_selection = 0;
5275         _editor->commit_reversible_selection_op ();
5276 }
5277
5278 void
5279 SelectionDrag::aborted (bool)
5280 {
5281         /* XXX: TODO */
5282 }
5283
5284 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
5285         : Drag (e, i, false),
5286           _operation (o),
5287           _copy (false)
5288 {
5289         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
5290
5291         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group,
5292                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
5293                                                                       physical_screen_height (_editor->current_toplevel()->get_window())));
5294         _drag_rect->hide ();
5295
5296         _drag_rect->set_fill_color (UIConfiguration::instance().color ("range drag rect"));
5297         _drag_rect->set_outline_color (UIConfiguration::instance().color ("range drag rect"));
5298 }
5299
5300 RangeMarkerBarDrag::~RangeMarkerBarDrag()
5301 {
5302         /* normal canvas items will be cleaned up when their parent group is deleted. But
5303            this item is created as the child of a long-lived parent group, and so we
5304            need to explicitly delete it.
5305         */
5306         delete _drag_rect;
5307 }
5308
5309 void
5310 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5311 {
5312         if (_editor->session() == 0) {
5313                 return;
5314         }
5315
5316         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
5317
5318         if (!_editor->temp_location) {
5319                 _editor->temp_location = new Location (*_editor->session());
5320         }
5321
5322         switch (_operation) {
5323         case CreateSkipMarker:
5324         case CreateRangeMarker:
5325         case CreateTransportMarker:
5326         case CreateCDMarker:
5327
5328                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
5329                         _copy = true;
5330                 } else {
5331                         _copy = false;
5332                 }
5333                 cursor = _editor->cursors()->selector;
5334                 break;
5335         }
5336
5337         Drag::start_grab (event, cursor);
5338
5339         show_verbose_cursor_time (adjusted_current_frame (event));
5340 }
5341
5342 void
5343 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
5344 {
5345         framepos_t start = 0;
5346         framepos_t end = 0;
5347         ArdourCanvas::Rectangle *crect;
5348
5349         switch (_operation) {
5350         case CreateSkipMarker:
5351                 crect = _editor->range_bar_drag_rect;
5352                 break;
5353         case CreateRangeMarker:
5354                 crect = _editor->range_bar_drag_rect;
5355                 break;
5356         case CreateTransportMarker:
5357                 crect = _editor->transport_bar_drag_rect;
5358                 break;
5359         case CreateCDMarker:
5360                 crect = _editor->cd_marker_bar_drag_rect;
5361                 break;
5362         default:
5363                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
5364                 return;
5365                 break;
5366         }
5367
5368         framepos_t const pf = adjusted_current_frame (event);
5369
5370         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
5371                 framepos_t grab = grab_frame ();
5372                 _editor->snap_to (grab);
5373
5374                 if (pf < grab_frame()) {
5375                         start = pf;
5376                         end = grab;
5377                 } else {
5378                         end = pf;
5379                         start = grab;
5380                 }
5381
5382                 /* first drag: Either add to the selection
5383                    or create a new selection.
5384                 */
5385
5386                 if (first_move) {
5387
5388                         _editor->temp_location->set (start, end);
5389
5390                         crect->show ();
5391
5392                         update_item (_editor->temp_location);
5393                         _drag_rect->show();
5394                         //_drag_rect->raise_to_top();
5395
5396                 }
5397         }
5398
5399         if (start != end) {
5400                 _editor->temp_location->set (start, end);
5401
5402                 double x1 = _editor->sample_to_pixel (start);
5403                 double x2 = _editor->sample_to_pixel (end);
5404                 crect->set_x0 (x1);
5405                 crect->set_x1 (x2);
5406
5407                 update_item (_editor->temp_location);
5408         }
5409
5410         show_verbose_cursor_time (pf);
5411
5412 }
5413
5414 void
5415 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
5416 {
5417         Location * newloc = 0;
5418         string rangename;
5419         int flags;
5420
5421         if (movement_occurred) {
5422                 motion (event, false);
5423                 _drag_rect->hide();
5424
5425                 switch (_operation) {
5426                 case CreateSkipMarker:
5427                 case CreateRangeMarker:
5428                 case CreateCDMarker:
5429                     {
5430                         XMLNode &before = _editor->session()->locations()->get_state();
5431                         if (_operation == CreateSkipMarker) {
5432                                 _editor->begin_reversible_command (_("new skip marker"));
5433                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
5434                                 flags = Location::IsRangeMarker | Location::IsSkip;
5435                                 _editor->range_bar_drag_rect->hide();
5436                         } else if (_operation == CreateCDMarker) {
5437                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
5438                                 _editor->begin_reversible_command (_("new CD marker"));
5439                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
5440                                 _editor->cd_marker_bar_drag_rect->hide();
5441                         } else {
5442                                 _editor->begin_reversible_command (_("new skip marker"));
5443                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
5444                                 flags = Location::IsRangeMarker;
5445                                 _editor->range_bar_drag_rect->hide();
5446                         }
5447                         newloc = new Location (
5448                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
5449                                 );
5450
5451                         _editor->session()->locations()->add (newloc, true);
5452                         XMLNode &after = _editor->session()->locations()->get_state();
5453                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
5454                         _editor->commit_reversible_command ();
5455                         break;
5456                     }
5457
5458                 case CreateTransportMarker:
5459                         // popup menu to pick loop or punch
5460                         _editor->new_transport_marker_context_menu (&event->button, _item);
5461                         break;
5462                 }
5463
5464         } else {
5465
5466                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
5467
5468                 if (_operation == CreateTransportMarker) {
5469
5470                         /* didn't drag, so just locate */
5471
5472                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
5473
5474                 } else if (_operation == CreateCDMarker) {
5475
5476                         /* didn't drag, but mark is already created so do
5477                          * nothing */
5478
5479                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
5480
5481                         framepos_t start;
5482                         framepos_t end;
5483
5484                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
5485
5486                         if (end == max_framepos) {
5487                                 end = _editor->session()->current_end_frame ();
5488                         }
5489
5490                         if (start == max_framepos) {
5491                                 start = _editor->session()->current_start_frame ();
5492                         }
5493
5494                         switch (_editor->mouse_mode) {
5495                         case MouseObject:
5496                                 /* find the two markers on either side and then make the selection from it */
5497                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
5498                                 break;
5499
5500                         case MouseRange:
5501                                 /* find the two markers on either side of the click and make the range out of it */
5502                                 _editor->selection->set (start, end);
5503                                 break;
5504
5505                         default:
5506                                 break;
5507                         }
5508                 }
5509         }
5510
5511         _editor->stop_canvas_autoscroll ();
5512 }
5513
5514 void
5515 RangeMarkerBarDrag::aborted (bool movement_occurred)
5516 {
5517         if (movement_occurred) {
5518                 _drag_rect->hide ();
5519         }
5520 }
5521
5522 void
5523 RangeMarkerBarDrag::update_item (Location* location)
5524 {
5525         double const x1 = _editor->sample_to_pixel (location->start());
5526         double const x2 = _editor->sample_to_pixel (location->end());
5527
5528         _drag_rect->set_x0 (x1);
5529         _drag_rect->set_x1 (x2);
5530 }
5531
5532 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
5533         : Drag (e, i)
5534         , _cumulative_dx (0)
5535         , _cumulative_dy (0)
5536         , _was_selected (false)
5537 {
5538         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
5539
5540         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
5541         assert (_primary);
5542         _region = &_primary->region_view ();
5543         _note_height = _region->midi_stream_view()->note_height ();
5544 }
5545
5546 void
5547 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
5548 {
5549         Drag::start_grab (event);
5550         setup_snap_delta (_region->source_beats_to_absolute_frames (_primary->note()->time ()));
5551
5552         if (!(_was_selected = _primary->selected())) {
5553
5554                 /* tertiary-click means extend selection - we'll do that on button release,
5555                    so don't add it here, because otherwise we make it hard to figure
5556                    out the "extend-to" range.
5557                 */
5558
5559                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
5560
5561                 if (!extend) {
5562                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
5563
5564                         if (add) {
5565                                 _region->note_selected (_primary, true);
5566                         } else {
5567                                 _editor->get_selection().clear_points();
5568                                 _region->unique_select (_primary);
5569                         }
5570                 }
5571         }
5572 }
5573
5574 /** @return Current total drag x change in frames */
5575 frameoffset_t
5576 NoteDrag::total_dx (const guint state) const
5577 {
5578         if (_x_constrained) {
5579                 return 0;
5580         }
5581         TempoMap& map (_editor->session()->tempo_map());
5582
5583         /* dx in frames */
5584         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
5585
5586         /* primary note time */
5587         double const quarter_note_start = map.quarter_note_at_beat (_region->region()->beat() - _region->midi_region()->start_beats().to_double());
5588         frameoffset_t const n = map.frame_at_quarter_note (quarter_note_start + _primary->note()->time().to_double());
5589
5590         /* new time of the primary note in session frames */
5591         frameoffset_t st = n + dx + snap_delta (state);
5592
5593         framepos_t const rp = _region->region()->position ();
5594
5595         /* prevent the note being dragged earlier than the region's position */
5596         st = max (st, rp);
5597
5598         /* possibly snap and return corresponding delta */
5599
5600         bool snap = true;
5601
5602         if (ArdourKeyboard::indicates_snap (state)) {
5603                 if (_editor->snap_mode () != SnapOff) {
5604                         snap = false;
5605                 }
5606         } else {
5607                 if (_editor->snap_mode () == SnapOff) {
5608                         snap = false;
5609                         /* inverted logic here - we;re in snapoff but we've pressed the snap delta modifier */
5610                         if (ArdourKeyboard::indicates_snap_delta (state)) {
5611                                 snap = true;
5612                         }
5613                 }
5614         }
5615
5616         frameoffset_t ret;
5617         if (snap) {
5618                 bool const ensure_snap = _editor->snap_mode () != SnapMagnetic;
5619                 ret =  _region->snap_frame_to_frame (st - rp, ensure_snap) + rp - n - snap_delta (state);
5620         } else {
5621                 ret = st - n - snap_delta (state);
5622         }
5623         return ret;
5624 }
5625
5626 /** @return Current total drag y change in note number */
5627 int8_t
5628 NoteDrag::total_dy () const
5629 {
5630         if (_y_constrained) {
5631                 return 0;
5632         }
5633
5634         MidiStreamView* msv = _region->midi_stream_view ();
5635         double const y = _region->midi_view()->y_position ();
5636         /* new current note */
5637         uint8_t n = msv->y_to_note (current_pointer_y () - y);
5638         /* clamp */
5639         n = max (msv->lowest_note(), n);
5640         n = min (msv->highest_note(), n);
5641         /* and work out delta */
5642         return n - msv->y_to_note (grab_y() - y);
5643 }
5644
5645 void
5646 NoteDrag::motion (GdkEvent * event, bool)
5647 {
5648         /* Total change in x and y since the start of the drag */
5649         frameoffset_t const dx = total_dx (event->button.state);
5650         int8_t const dy = total_dy ();
5651
5652         /* Now work out what we have to do to the note canvas items to set this new drag delta */
5653         double const tdx = _x_constrained ? 0 : _editor->sample_to_pixel (dx) - _cumulative_dx;
5654         double const tdy = _y_constrained ? 0 : -dy * _note_height - _cumulative_dy;
5655
5656         if (tdx || tdy) {
5657                 _cumulative_dx += tdx;
5658                 _cumulative_dy += tdy;
5659
5660                 int8_t note_delta = total_dy();
5661
5662                 if (tdx || tdy) {
5663                         _region->move_selection (tdx, tdy, note_delta);
5664
5665                         /* the new note value may be the same as the old one, but we
5666                          * don't know what that means because the selection may have
5667                          * involved more than one note and we might be doing something
5668                          * odd with them. so show the note value anyway, always.
5669                          */
5670
5671                         uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
5672
5673                         _region->show_verbose_cursor_for_new_note_value (_primary->note(), new_note);
5674                 }
5675         }
5676 }
5677
5678 void
5679 NoteDrag::finished (GdkEvent* ev, bool moved)
5680 {
5681         if (!moved) {
5682                 /* no motion - select note */
5683
5684                 if (_editor->current_mouse_mode() == Editing::MouseContent ||
5685                     _editor->current_mouse_mode() == Editing::MouseDraw) {
5686
5687                         bool changed = false;
5688
5689                         if (_was_selected) {
5690                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5691                                 if (add) {
5692                                         _region->note_deselected (_primary);
5693                                         changed = true;
5694                                 } else {
5695                                         _editor->get_selection().clear_points();
5696                                         _region->unique_select (_primary);
5697                                         changed = true;
5698                                 }
5699                         } else {
5700                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
5701                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
5702
5703                                 if (!extend && !add && _region->selection_size() > 1) {
5704                                         _editor->get_selection().clear_points();
5705                                         _region->unique_select (_primary);
5706                                         changed = true;
5707                                 } else if (extend) {
5708                                         _region->note_selected (_primary, true, true);
5709                                         changed = true;
5710                                 } else {
5711                                         /* it was added during button press */
5712                                         changed = true;
5713
5714                                 }
5715                         }
5716
5717                         if (changed) {
5718                                 _editor->begin_reversible_selection_op(X_("Select Note Release"));
5719                                 _editor->commit_reversible_selection_op();
5720                         }
5721                 }
5722         } else {
5723                 _region->note_dropped (_primary, total_dx (ev->button.state), total_dy());
5724         }
5725 }
5726
5727 void
5728 NoteDrag::aborted (bool)
5729 {
5730         /* XXX: TODO */
5731 }
5732
5733 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
5734 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
5735         : Drag (editor, atv->base_item ())
5736         , _ranges (r)
5737         , _y_origin (atv->y_position())
5738         , _nothing_to_drag (false)
5739 {
5740         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5741         setup (atv->lines ());
5742 }
5743
5744 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
5745 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
5746         : Drag (editor, rv->get_canvas_group ())
5747         , _ranges (r)
5748         , _y_origin (rv->get_time_axis_view().y_position())
5749         , _nothing_to_drag (false)
5750         , _integral (false)
5751 {
5752         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
5753
5754         list<boost::shared_ptr<AutomationLine> > lines;
5755
5756         AudioRegionView*      audio_view;
5757         AutomationRegionView* automation_view;
5758         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
5759                 lines.push_back (audio_view->get_gain_line ());
5760         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
5761                 lines.push_back (automation_view->line ());
5762                 _integral = true;
5763         } else {
5764                 error << _("Automation range drag created for invalid region type") << endmsg;
5765         }
5766
5767         setup (lines);
5768 }
5769
5770 /** @param lines AutomationLines to drag.
5771  *  @param offset Offset from the session start to the points in the AutomationLines.
5772  */
5773 void
5774 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
5775 {
5776         /* find the lines that overlap the ranges being dragged */
5777         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
5778         while (i != lines.end ()) {
5779                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
5780                 ++j;
5781
5782                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
5783
5784                 /* check this range against all the AudioRanges that we are using */
5785                 list<AudioRange>::const_iterator k = _ranges.begin ();
5786                 while (k != _ranges.end()) {
5787                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
5788                                 break;
5789                         }
5790                         ++k;
5791                 }
5792
5793                 /* add it to our list if it overlaps at all */
5794                 if (k != _ranges.end()) {
5795                         Line n;
5796                         n.line = *i;
5797                         n.state = 0;
5798                         n.range = r;
5799                         _lines.push_back (n);
5800                 }
5801
5802                 i = j;
5803         }
5804
5805         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
5806 }
5807
5808 double
5809 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
5810 {
5811         return 1.0 - ((global_y - _y_origin) / line->height());
5812 }
5813
5814 double
5815 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
5816 {
5817         const double v = list->eval(x);
5818         return _integral ? rint(v) : v;
5819 }
5820
5821 void
5822 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5823 {
5824         Drag::start_grab (event, cursor);
5825
5826         /* Get line states before we start changing things */
5827         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5828                 i->state = &i->line->get_state ();
5829                 i->original_fraction = y_fraction (i->line, current_pointer_y());
5830         }
5831
5832         if (_ranges.empty()) {
5833
5834                 /* No selected time ranges: drag all points */
5835                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5836                         uint32_t const N = i->line->npoints ();
5837                         for (uint32_t j = 0; j < N; ++j) {
5838                                 i->points.push_back (i->line->nth (j));
5839                         }
5840                 }
5841
5842         }
5843
5844         if (_nothing_to_drag) {
5845                 return;
5846         }
5847 }
5848
5849 void
5850 AutomationRangeDrag::motion (GdkEvent*, bool first_move)
5851 {
5852         if (_nothing_to_drag && !first_move) {
5853                 return;
5854         }
5855
5856         if (first_move) {
5857                 _editor->begin_reversible_command (_("automation range move"));
5858
5859                 if (!_ranges.empty()) {
5860
5861                         for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5862
5863                                 framecnt_t const half = (i->start + i->end) / 2;
5864
5865                                 /* find the line that this audio range starts in */
5866                                 list<Line>::iterator j = _lines.begin();
5867                                 while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5868                                         ++j;
5869                                 }
5870
5871                                 if (j != _lines.end()) {
5872                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5873
5874                                 /* j is the line that this audio range starts in; fade into it;
5875                                    64 samples length plucked out of thin air.
5876                                 */
5877
5878                                         framepos_t a = i->start + 64;
5879                                         if (a > half) {
5880                                                 a = half;
5881                                         }
5882
5883                                         double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5884                                         double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5885
5886                                         XMLNode &before = the_list->get_state();
5887                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5888                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5889
5890                                         if (add_p || add_q) {
5891                                                 _editor->session()->add_command (
5892                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5893                                         }
5894                                 }
5895
5896                                 /* same thing for the end */
5897
5898                                 j = _lines.begin();
5899                                 while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5900                                         ++j;
5901                                 }
5902
5903                                 if (j != _lines.end()) {
5904                                         boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5905
5906                                         /* j is the line that this audio range starts in; fade out of it;
5907                                            64 samples length plucked out of thin air.
5908                                         */
5909
5910                                         framepos_t b = i->end - 64;
5911                                         if (b < half) {
5912                                                 b = half;
5913                                         }
5914
5915                                         double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5916                                         double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5917
5918                                         XMLNode &before = the_list->get_state();
5919                                         bool const add_p = the_list->editor_add (p, value (the_list, p), false);
5920                                         bool const add_q = the_list->editor_add (q, value (the_list, q), false);
5921
5922                                         if (add_p || add_q) {
5923                                                 _editor->session()->add_command (
5924                                                         new MementoCommand<AutomationList>(*the_list.get (), &before, &the_list->get_state()));
5925                                         }
5926                                 }
5927                         }
5928
5929                         _nothing_to_drag = true;
5930
5931                         /* Find all the points that should be dragged and put them in the relevant
5932                            points lists in the Line structs.
5933                         */
5934
5935                         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5936
5937                                 uint32_t const N = i->line->npoints ();
5938                                 for (uint32_t j = 0; j < N; ++j) {
5939
5940                                         /* here's a control point on this line */
5941                                         ControlPoint* p = i->line->nth (j);
5942                                         double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5943
5944                                         /* see if it's inside a range */
5945                                         list<AudioRange>::const_iterator k = _ranges.begin ();
5946                                         while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5947                                                 ++k;
5948                                         }
5949
5950                                         if (k != _ranges.end()) {
5951                                                 /* dragging this point */
5952                                                 _nothing_to_drag = false;
5953                                                 i->points.push_back (p);
5954                                         }
5955                                 }
5956                         }
5957                 }
5958
5959                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5960                         i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5961                 }
5962         }
5963
5964         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5965                 float const f = y_fraction (l->line, current_pointer_y());
5966                 /* we are ignoring x position for this drag, so we can just pass in anything */
5967                 pair<double, float> result;
5968                 uint32_t ignored;
5969                 result = l->line->drag_motion (0, f, true, false, ignored);
5970                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, result.second));
5971         }
5972 }
5973
5974 void
5975 AutomationRangeDrag::finished (GdkEvent* event, bool motion_occurred)
5976 {
5977         if (_nothing_to_drag || !motion_occurred) {
5978                 return;
5979         }
5980
5981         motion (event, false);
5982         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5983                 i->line->end_drag (false, 0);
5984         }
5985
5986         _editor->commit_reversible_command ();
5987 }
5988
5989 void
5990 AutomationRangeDrag::aborted (bool)
5991 {
5992         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5993                 i->line->reset ();
5994         }
5995 }
5996
5997 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5998         : view (v)
5999         , initial_time_axis_view (itav)
6000 {
6001         /* note that time_axis_view may be null if the regionview was created
6002          * as part of a copy operation.
6003          */
6004         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
6005         layer = v->region()->layer ();
6006         initial_y = v->get_canvas_group()->position().y;
6007         initial_playlist = v->region()->playlist ();
6008         initial_position = v->region()->position ();
6009         initial_end = v->region()->position () + v->region()->length ();
6010 }
6011
6012 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
6013         : Drag (e, i->canvas_item ())
6014         , _region_view (r)
6015         , _patch_change (i)
6016         , _cumulative_dx (0)
6017 {
6018         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
6019                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
6020                                                    grab_frame()));
6021 }
6022
6023 void
6024 PatchChangeDrag::motion (GdkEvent* ev, bool)
6025 {
6026         framepos_t f = adjusted_current_frame (ev);
6027         boost::shared_ptr<Region> r = _region_view->region ();
6028         f = max (f, r->position ());
6029         f = min (f, r->last_frame ());
6030
6031         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
6032         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
6033         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
6034         _cumulative_dx = dxu;
6035 }
6036
6037 void
6038 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
6039 {
6040         if (!movement_occurred) {
6041                 if (was_double_click()) {
6042                         _region_view->edit_patch_change (_patch_change);
6043                 }
6044                 return;
6045         }
6046
6047         boost::shared_ptr<Region> r (_region_view->region ());
6048         framepos_t f = adjusted_current_frame (ev);
6049         f = max (f, r->position ());
6050         f = min (f, r->last_frame ());
6051
6052         _region_view->move_patch_change (
6053                 *_patch_change,
6054                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
6055                 );
6056 }
6057
6058 void
6059 PatchChangeDrag::aborted (bool)
6060 {
6061         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
6062 }
6063
6064 void
6065 PatchChangeDrag::setup_pointer_frame_offset ()
6066 {
6067         boost::shared_ptr<Region> region = _region_view->region ();
6068         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
6069 }
6070
6071 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
6072         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6073         , _region_view (rv)
6074 {
6075
6076 }
6077
6078 void
6079 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
6080 {
6081         _region_view->update_drag_selection (
6082                 x1, x2, y1, y2,
6083                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
6084 }
6085
6086 void
6087 MidiRubberbandSelectDrag::deselect_things ()
6088 {
6089         /* XXX */
6090 }
6091
6092 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
6093         : RubberbandSelectDrag (e, rv->get_canvas_group ())
6094         , _region_view (rv)
6095 {
6096         _vertical_only = true;
6097 }
6098
6099 void
6100 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
6101 {
6102         double const y = _region_view->midi_view()->y_position ();
6103
6104         y1 = max (0.0, y1 - y);
6105         y2 = max (0.0, y2 - y);
6106
6107         _region_view->update_vertical_drag_selection (
6108                 y1,
6109                 y2,
6110                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
6111                 );
6112 }
6113
6114 void
6115 MidiVerticalSelectDrag::deselect_things ()
6116 {
6117         /* XXX */
6118 }
6119
6120 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
6121         : RubberbandSelectDrag (e, i)
6122 {
6123
6124 }
6125
6126 void
6127 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
6128 {
6129         if (drag_in_progress) {
6130                 /* We just want to select things at the end of the drag, not during it */
6131                 return;
6132         }
6133
6134         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
6135
6136         _editor->begin_reversible_selection_op (X_("rubberband selection"));
6137
6138         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
6139
6140         _editor->commit_reversible_selection_op ();
6141 }
6142
6143 void
6144 EditorRubberbandSelectDrag::deselect_things ()
6145 {
6146         _editor->begin_reversible_selection_op (X_("Clear Selection (rubberband)"));
6147
6148         _editor->selection->clear_tracks();
6149         _editor->selection->clear_regions();
6150         _editor->selection->clear_points ();
6151         _editor->selection->clear_lines ();
6152         _editor->selection->clear_midi_notes ();
6153
6154         _editor->commit_reversible_selection_op();
6155 }
6156
6157 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
6158         : Drag (e, i)
6159         , _region_view (rv)
6160         , _drag_rect (0)
6161 {
6162         _note[0] = _note[1] = 0;
6163 }
6164
6165 NoteCreateDrag::~NoteCreateDrag ()
6166 {
6167         delete _drag_rect;
6168 }
6169
6170 framecnt_t
6171 NoteCreateDrag::grid_frames (framepos_t t) const
6172 {
6173         bool success;
6174         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
6175         if (!success) {
6176                 grid_beats = Evoral::Beats(1);
6177         }
6178
6179         return _region_view->region_beats_to_region_frames (grid_beats);
6180 }
6181
6182 void
6183 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
6184 {
6185         Drag::start_grab (event, cursor);
6186
6187         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
6188         TempoMap& map (_editor->session()->tempo_map());
6189
6190         const framepos_t pf = _drags->current_pointer_frame ();
6191         const int32_t divisions = _editor->get_grid_music_divisions (event->button.state);
6192
6193         double eqaf = map.exact_qn_at_frame (pf, divisions);
6194
6195         if (divisions != 0) {
6196                 bool success = false;
6197                 Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, pf);
6198                 if (!success) {
6199                         grid_beats = Evoral::Beats(1);
6200                 }
6201
6202                 const double qaf = map.quarter_note_at_frame (pf);
6203
6204                 /* Hack so that we always snap to the note that we are over, instead of snapping
6205                    to the next one if we're more than halfway through the one we're over.
6206                 */
6207
6208                 const double rem = eqaf - qaf;
6209                 if (rem >= 0.0 && eqaf - grid_beats.to_double() > _region_view->region()->pulse() * 4.0) {
6210                         eqaf -= grid_beats.to_double();
6211                 }
6212         }
6213
6214         _note[0] = map.frame_at_quarter_note (eqaf) - _region_view->region()->position();
6215         _note[1] = _note[0];
6216
6217         MidiStreamView* sv = _region_view->midi_stream_view ();
6218         double const x = _editor->sample_to_pixel (_note[0]);
6219         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
6220
6221         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
6222         _drag_rect->set_outline_all ();
6223         _drag_rect->set_outline_color (0xffffff99);
6224         _drag_rect->set_fill_color (0xffffff66);
6225 }
6226
6227 void
6228 NoteCreateDrag::motion (GdkEvent* event, bool)
6229 {
6230         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
6231         double const x0 = _editor->sample_to_pixel (_note[0]);
6232         double const x1 = _editor->sample_to_pixel (_note[1]);
6233         _drag_rect->set_x0 (std::min(x0, x1));
6234         _drag_rect->set_x1 (std::max(x0, x1));
6235 }
6236
6237 void
6238 NoteCreateDrag::finished (GdkEvent* ev, bool had_movement)
6239 {
6240         if (!had_movement) {
6241                 return;
6242         }
6243
6244         framepos_t const start = min (_note[0], _note[1]);
6245         framepos_t const start_sess_rel = start + _region_view->region()->position();
6246         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
6247
6248         framecnt_t const g = grid_frames (start);
6249         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
6250
6251         if (_editor->get_grid_music_divisions (ev->button.state) != 0 && length < g) {
6252                 length = g;
6253         }
6254
6255         TempoMap& map (_editor->session()->tempo_map());
6256         const double qn_length = map.quarter_note_at_frame (start_sess_rel + length) - map.quarter_note_at_frame (start_sess_rel);
6257
6258         Evoral::Beats qn_length_beats = max (one_tick, Evoral::Beats (qn_length));
6259         _region_view->create_note_at (start, _drag_rect->y0(), qn_length_beats, ev->button.state, false);
6260 }
6261
6262 double
6263 NoteCreateDrag::y_to_region (double y) const
6264 {
6265         double x = 0;
6266         _region_view->get_canvas_group()->canvas_to_item (x, y);
6267         return y;
6268 }
6269
6270 void
6271 NoteCreateDrag::aborted (bool)
6272 {
6273
6274 }
6275
6276 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
6277         : Drag (e, i)
6278         , arv (rv)
6279         , start (start_yn)
6280 {
6281         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
6282 }
6283
6284 void
6285 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
6286 {
6287         Drag::start_grab (event, cursor);
6288 }
6289
6290 void
6291 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
6292 {
6293         double distance;
6294         double new_length;
6295         framecnt_t len;
6296
6297         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6298
6299         if (start) {
6300                 distance = _drags->current_pointer_x() - grab_x();
6301                 len = ar->fade_in()->back()->when;
6302         } else {
6303                 distance = grab_x() - _drags->current_pointer_x();
6304                 len = ar->fade_out()->back()->when;
6305         }
6306
6307         /* how long should it be ? */
6308
6309         new_length = len + _editor->pixel_to_sample (distance);
6310
6311         /* now check with the region that this is legal */
6312
6313         new_length = ar->verify_xfade_bounds (new_length, start);
6314
6315         if (start) {
6316                 arv->reset_fade_in_shape_width (ar, new_length);
6317         } else {
6318                 arv->reset_fade_out_shape_width (ar, new_length);
6319         }
6320 }
6321
6322 void
6323 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
6324 {
6325         double distance;
6326         double new_length;
6327         framecnt_t len;
6328
6329         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
6330
6331         if (start) {
6332                 distance = _drags->current_pointer_x() - grab_x();
6333                 len = ar->fade_in()->back()->when;
6334         } else {
6335                 distance = grab_x() - _drags->current_pointer_x();
6336                 len = ar->fade_out()->back()->when;
6337         }
6338
6339         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
6340
6341         _editor->begin_reversible_command ("xfade trim");
6342         ar->playlist()->clear_owned_changes ();
6343
6344         if (start) {
6345                 ar->set_fade_in_length (new_length);
6346         } else {
6347                 ar->set_fade_out_length (new_length);
6348         }
6349
6350         /* Adjusting the xfade may affect other regions in the playlist, so we need
6351            to get undo Commands from the whole playlist rather than just the
6352            region.
6353         */
6354
6355         vector<Command*> cmds;
6356         ar->playlist()->rdiff (cmds);
6357         _editor->session()->add_commands (cmds);
6358         _editor->commit_reversible_command ();
6359
6360 }
6361
6362 void
6363 CrossfadeEdgeDrag::aborted (bool)
6364 {
6365         if (start) {
6366                 // arv->redraw_start_xfade ();
6367         } else {
6368                 // arv->redraw_end_xfade ();
6369         }
6370 }
6371
6372 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
6373         : Drag (e, item, true)
6374         , line (new EditorCursor (*e))
6375 {
6376         line->set_position (pos);
6377         line->show ();
6378 }
6379
6380 RegionCutDrag::~RegionCutDrag ()
6381 {
6382         delete line;
6383 }
6384
6385 void
6386 RegionCutDrag::motion (GdkEvent*, bool)
6387 {
6388         framepos_t where = _drags->current_pointer_frame();
6389         _editor->snap_to (where);
6390
6391         line->set_position (where);
6392 }
6393
6394 void
6395 RegionCutDrag::finished (GdkEvent* event, bool)
6396 {
6397         _editor->get_track_canvas()->canvas()->re_enter();
6398
6399         framepos_t pos = _drags->current_pointer_frame();
6400
6401         line->hide ();
6402
6403         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
6404
6405         if (rs.empty()) {
6406                 return;
6407         }
6408
6409         _editor->split_regions_at (pos, rs, _editor->get_grid_music_divisions (event->button.state));
6410 }
6411
6412 void
6413 RegionCutDrag::aborted (bool)
6414 {
6415 }