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