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