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