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