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