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