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