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