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