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