implement MarkerDrag::abort()
[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 movement_occured)
3544 {
3545         if (!movement_occured) {
3546                 return;
3547         }
3548
3549         for (CopiedLocationInfo::iterator x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3550
3551                 /* move all markers to their original location */
3552                 
3553                 for (vector<Marker*>::iterator m = x->markers.begin(); m != x->markers.end(); ++m) {
3554                         (*m)->set_position ((*m)->location()->start(), (*m)->location()->end());
3555                 }
3556         }
3557 }
3558
3559 void
3560 MarkerDrag::update_item (Location*)
3561 {
3562         /* noop */
3563 }
3564
3565 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3566         : Drag (e, i),
3567           _cumulative_x_drag (0),
3568           _cumulative_y_drag (0)
3569 {
3570         if (_zero_gain_fraction < 0.0) {
3571                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3572         }
3573
3574         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3575
3576         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3577         assert (_point);
3578 }
3579
3580
3581 void
3582 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3583 {
3584         Drag::start_grab (event, _editor->cursors()->fader);
3585
3586         // start the grab at the center of the control point so
3587         // the point doesn't 'jump' to the mouse after the first drag
3588         _fixed_grab_x = _point->get_x();
3589         _fixed_grab_y = _point->get_y();
3590
3591         float const fraction = 1 - (_point->get_y() / _point->line().height());
3592
3593         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3594
3595         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3596
3597         _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3598
3599         if (!_point->can_slide ()) {
3600                 _x_constrained = true;
3601         }
3602 }
3603
3604 void
3605 ControlPointDrag::motion (GdkEvent* event, bool)
3606 {
3607         double dx = _drags->current_pointer_x() - last_pointer_x();
3608         double dy = current_pointer_y() - last_pointer_y();
3609
3610         if (event->button.state & Keyboard::SecondaryModifier) {
3611                 dx *= 0.1;
3612                 dy *= 0.1;
3613         }
3614
3615         /* coordinate in pixels relative to the start of the region (for region-based automation)
3616            or track (for track-based automation) */
3617         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3618         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3619
3620         // calculate zero crossing point. back off by .01 to stay on the
3621         // positive side of zero
3622         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3623
3624         // make sure we hit zero when passing through
3625         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3626                 cy = zero_gain_y;
3627         }
3628
3629         if (_x_constrained) {
3630                 cx = _fixed_grab_x;
3631         }
3632         if (_y_constrained) {
3633                 cy = _fixed_grab_y;
3634         }
3635
3636         _cumulative_x_drag = cx - _fixed_grab_x;
3637         _cumulative_y_drag = cy - _fixed_grab_y;
3638
3639         cx = max (0.0, cx);
3640         cy = max (0.0, cy);
3641         cy = min ((double) _point->line().height(), cy);
3642
3643         framepos_t cx_frames = _editor->pixel_to_sample (cx);
3644
3645         if (!_x_constrained) {
3646                 _editor->snap_to_with_modifier (cx_frames, event);
3647         }
3648
3649         cx_frames = min (cx_frames, _point->line().maximum_time());
3650
3651         float const fraction = 1.0 - (cy / _point->line().height());
3652
3653         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3654
3655         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3656 }
3657
3658 void
3659 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3660 {
3661         if (!movement_occurred) {
3662
3663                 /* just a click */
3664
3665                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3666                         _editor->reset_point_selection ();
3667                 }
3668
3669         } else {
3670                 motion (event, false);
3671         }
3672
3673         _point->line().end_drag (_pushing, _final_index);
3674         _editor->commit_reversible_command ();
3675 }
3676
3677 void
3678 ControlPointDrag::aborted (bool)
3679 {
3680         _point->line().reset ();
3681 }
3682
3683 bool
3684 ControlPointDrag::active (Editing::MouseMode m)
3685 {
3686         if (m == Editing::MouseDraw) {
3687                 /* always active in mouse draw */
3688                 return true;
3689         }
3690
3691         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3692         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3693 }
3694
3695 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3696         : Drag (e, i),
3697           _line (0),
3698           _cumulative_y_drag (0)
3699 {
3700         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3701 }
3702
3703 void
3704 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3705 {
3706         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3707         assert (_line);
3708
3709         _item = &_line->grab_item ();
3710
3711         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3712            origin, and ditto for y.
3713         */
3714
3715         double cx = event->button.x;
3716         double cy = event->button.y;
3717
3718         _line->parent_group().canvas_to_item (cx, cy);
3719
3720         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3721
3722         uint32_t before;
3723         uint32_t after;
3724
3725         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3726                 /* no adjacent points */
3727                 return;
3728         }
3729
3730         Drag::start_grab (event, _editor->cursors()->fader);
3731
3732         /* store grab start in parent frame */
3733
3734         _fixed_grab_x = cx;
3735         _fixed_grab_y = cy;
3736
3737         double fraction = 1.0 - (cy / _line->height());
3738
3739         _line->start_drag_line (before, after, fraction);
3740
3741         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3742 }
3743
3744 void
3745 LineDrag::motion (GdkEvent* event, bool)
3746 {
3747         double dy = current_pointer_y() - last_pointer_y();
3748
3749         if (event->button.state & Keyboard::SecondaryModifier) {
3750                 dy *= 0.1;
3751         }
3752
3753         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3754
3755         _cumulative_y_drag = cy - _fixed_grab_y;
3756
3757         cy = max (0.0, cy);
3758         cy = min ((double) _line->height(), cy);
3759
3760         double const fraction = 1.0 - (cy / _line->height());
3761         uint32_t ignored;
3762
3763         /* we are ignoring x position for this drag, so we can just pass in anything */
3764         _line->drag_motion (0, fraction, true, false, ignored);
3765
3766         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3767 }
3768
3769 void
3770 LineDrag::finished (GdkEvent* event, bool movement_occured)
3771 {
3772         if (movement_occured) {
3773                 motion (event, false);
3774                 _line->end_drag (false, 0);
3775         } else {
3776                 /* add a new control point on the line */
3777
3778                 AutomationTimeAxisView* atv;
3779
3780                 _line->end_drag (false, 0);
3781
3782                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3783                         framepos_t where = _editor->window_event_sample (event, 0, 0);
3784                         atv->add_automation_event (event, where, event->button.y, false);
3785                 }
3786         }
3787
3788         _editor->commit_reversible_command ();
3789 }
3790
3791 void
3792 LineDrag::aborted (bool)
3793 {
3794         _line->reset ();
3795 }
3796
3797 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3798         : Drag (e, i),
3799           _line (0),
3800           _cumulative_x_drag (0)
3801 {
3802         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3803 }
3804
3805 void
3806 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3807 {
3808         Drag::start_grab (event);
3809
3810         _line = reinterpret_cast<Line*> (_item);
3811         assert (_line);
3812
3813         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3814
3815         double cx = event->button.x;
3816         double cy = event->button.y;
3817
3818         _item->parent()->canvas_to_item (cx, cy);
3819
3820         /* store grab start in parent frame */
3821         _region_view_grab_x = cx;
3822
3823         _before = *(float*) _item->get_data ("position");
3824
3825         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3826
3827         _max_x = _editor->sample_to_pixel(_arv->get_duration());
3828 }
3829
3830 void
3831 FeatureLineDrag::motion (GdkEvent*, bool)
3832 {
3833         double dx = _drags->current_pointer_x() - last_pointer_x();
3834
3835         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3836
3837         _cumulative_x_drag += dx;
3838
3839         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3840
3841         if (cx > _max_x){
3842                 cx = _max_x;
3843         }
3844         else if(cx < 0){
3845                 cx = 0;
3846         }
3847
3848         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3849         assert (bbox);
3850         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3851
3852         float *pos = new float;
3853         *pos = cx;
3854
3855         _line->set_data ("position", pos);
3856
3857         _before = cx;
3858 }
3859
3860 void
3861 FeatureLineDrag::finished (GdkEvent*, bool)
3862 {
3863         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3864         _arv->update_transient(_before, _before);
3865 }
3866
3867 void
3868 FeatureLineDrag::aborted (bool)
3869 {
3870         //_line->reset ();
3871 }
3872
3873 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3874         : Drag (e, i)
3875         , _vertical_only (false)
3876 {
3877         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3878 }
3879
3880 void
3881 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3882 {
3883         Drag::start_grab (event);
3884         show_verbose_cursor_time (adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid()));
3885 }
3886
3887 void
3888 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3889 {
3890         framepos_t start;
3891         framepos_t end;
3892         double y1;
3893         double y2;
3894
3895         framepos_t const pf = adjusted_current_frame (event, ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid());
3896
3897         framepos_t grab = grab_frame ();
3898         if (ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
3899                 _editor->snap_to_with_modifier (grab, event);
3900         } else {
3901                 grab = raw_grab_frame ();
3902         }
3903
3904         /* base start and end on initial click position */
3905
3906         if (pf < grab) {
3907                 start = pf;
3908                 end = grab;
3909         } else {
3910                 end = pf;
3911                 start = grab;
3912         }
3913
3914         if (current_pointer_y() < grab_y()) {
3915                 y1 = current_pointer_y();
3916                 y2 = grab_y();
3917         } else {
3918                 y2 = current_pointer_y();
3919                 y1 = grab_y();
3920         }
3921
3922         if (start != end || y1 != y2) {
3923
3924                 double x1 = _editor->sample_to_pixel (start);
3925                 double x2 = _editor->sample_to_pixel (end);
3926                 const double min_dimension = 2.0;
3927
3928                 if (_vertical_only) {
3929                         /* fixed 10 pixel width */
3930                         x2 = x1 + 10;
3931                 } else {
3932                         if (x2 < x1) {
3933                                 x2 = min (x1 - min_dimension, x2);
3934                         } else {
3935                                 x2 = max (x1 + min_dimension, x2);
3936                         }
3937                 } 
3938
3939                 if (y2 < y1) {
3940                         y2 = min (y1 - min_dimension, y2);
3941                 } else {
3942                         y2 = max (y1 + min_dimension, y2);
3943                 }
3944
3945                 /* translate rect into item space and set */
3946
3947                 ArdourCanvas::Rect r (x1, y1, x2, y2);
3948
3949                 /* this drag is a _trackview_only == true drag, so the y1 and
3950                  * y2 (computed using current_pointer_y() and grab_y()) will be
3951                  * relative to the top of the trackview group). The
3952                  * rubberband rect has the same parent/scroll offset as the
3953                  * the trackview group, so we can use the "r" rect directly
3954                  * to set the shape of the rubberband.
3955                  */
3956
3957                 _editor->rubberband_rect->set (r);
3958                 _editor->rubberband_rect->show();
3959                 _editor->rubberband_rect->raise_to_top();
3960
3961                 show_verbose_cursor_time (pf);
3962
3963                 do_select_things (event, true);
3964         }
3965 }
3966
3967 void
3968 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3969 {
3970         framepos_t x1;
3971         framepos_t x2;
3972         framepos_t grab = grab_frame ();
3973         framepos_t lpf = last_pointer_frame ();
3974
3975         if (!ARDOUR_UI::config()->get_rubberbanding_snaps_to_grid ()) {
3976                 grab = raw_grab_frame ();
3977                 lpf = _editor->pixel_to_sample_from_event (last_pointer_x());
3978         }
3979         
3980         if (grab < lpf) {
3981                 x1 = grab;
3982                 x2 = lpf;
3983         } else {
3984                 x2 = grab;
3985                 x1 = lpf;
3986         }
3987
3988         double y1;
3989         double y2;
3990         
3991         if (current_pointer_y() < grab_y()) {
3992                 y1 = current_pointer_y();
3993                 y2 = grab_y();
3994         } else {
3995                 y2 = current_pointer_y();
3996                 y1 = grab_y();
3997         }
3998
3999         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
4000 }
4001
4002 void
4003 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
4004 {
4005         if (movement_occurred) {
4006
4007                 motion (event, false);
4008                 do_select_things (event, false);
4009
4010         } else {
4011
4012                 /* just a click */
4013
4014                 bool do_deselect = true;
4015                 MidiTimeAxisView* mtv;
4016
4017                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
4018                         /* MIDI track */
4019                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
4020                                 /* nothing selected */
4021                                 add_midi_region (mtv);
4022                                 do_deselect = false;
4023                         }
4024                 } 
4025
4026                 /* do not deselect if Primary or Tertiary (toggle-select or
4027                  * extend-select are pressed.
4028                  */
4029
4030                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) && 
4031                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) && 
4032                     do_deselect) {
4033                         deselect_things ();
4034                 }
4035
4036         }
4037
4038         _editor->rubberband_rect->hide();
4039 }
4040
4041 void
4042 RubberbandSelectDrag::aborted (bool)
4043 {
4044         _editor->rubberband_rect->hide ();
4045 }
4046
4047 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4048         : RegionDrag (e, i, p, v)
4049 {
4050         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4051 }
4052
4053 void
4054 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4055 {
4056         Drag::start_grab (event, cursor);
4057
4058         show_verbose_cursor_time (adjusted_current_frame (event));
4059 }
4060
4061 void
4062 TimeFXDrag::motion (GdkEvent* event, bool)
4063 {
4064         RegionView* rv = _primary;
4065         StreamView* cv = rv->get_time_axis_view().view ();
4066
4067         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4068         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4069         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4070
4071         framepos_t const pf = adjusted_current_frame (event);
4072
4073         if (pf > rv->region()->position()) {
4074                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4075         }
4076
4077         show_verbose_cursor_time (pf);
4078 }
4079
4080 void
4081 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4082 {
4083         _primary->get_time_axis_view().hide_timestretch ();
4084
4085         if (!movement_occurred) {
4086                 return;
4087         }
4088
4089         if (last_pointer_frame() < _primary->region()->position()) {
4090                 /* backwards drag of the left edge - not usable */
4091                 return;
4092         }
4093
4094         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4095
4096         float percentage = (double) newlen / (double) _primary->region()->length();
4097
4098 #ifndef USE_RUBBERBAND
4099         // Soundtouch uses percentage / 100 instead of normal (/ 1)
4100         if (_primary->region()->data_type() == DataType::AUDIO) {
4101                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4102         }
4103 #endif
4104
4105         if (!_editor->get_selection().regions.empty()) {
4106                 /* primary will already be included in the selection, and edit
4107                    group shared editing will propagate selection across
4108                    equivalent regions, so just use the current region
4109                    selection.
4110                 */
4111
4112                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4113                         error << _("An error occurred while executing time stretch operation") << endmsg;
4114                 }
4115         }
4116 }
4117
4118 void
4119 TimeFXDrag::aborted (bool)
4120 {
4121         _primary->get_time_axis_view().hide_timestretch ();
4122 }
4123
4124 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4125         : Drag (e, i)
4126 {
4127         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4128 }
4129
4130 void
4131 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4132 {
4133         Drag::start_grab (event);
4134 }
4135
4136 void
4137 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4138 {
4139         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4140 }
4141
4142 void
4143 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4144 {
4145         if (movement_occurred && _editor->session()) {
4146                 /* make sure we stop */
4147                 _editor->session()->request_transport_speed (0.0);
4148         }
4149 }
4150
4151 void
4152 ScrubDrag::aborted (bool)
4153 {
4154         /* XXX: TODO */
4155 }
4156
4157 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4158         : Drag (e, i)
4159         , _operation (o)
4160         , _add (false)
4161         , _original_pointer_time_axis (-1)
4162         , _time_selection_at_start (!_editor->get_selection().time.empty())
4163 {
4164         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4165         
4166         if (_time_selection_at_start) {
4167                 start_at_start = _editor->get_selection().time.start();
4168                 end_at_start = _editor->get_selection().time.end_frame();
4169         }
4170 }
4171
4172 void
4173 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4174 {
4175         if (_editor->session() == 0) {
4176                 return;
4177         }
4178
4179         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4180
4181         switch (_operation) {
4182         case CreateSelection:
4183                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4184                         _add = true;
4185                 } else {
4186                         _add = false;
4187                 }
4188                 cursor = _editor->cursors()->selector;
4189                 Drag::start_grab (event, cursor);
4190                 break;
4191
4192         case SelectionStartTrim:
4193                 if (_editor->clicked_axisview) {
4194                         _editor->clicked_axisview->order_selection_trims (_item, true);
4195                 }
4196                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4197                 break;
4198
4199         case SelectionEndTrim:
4200                 if (_editor->clicked_axisview) {
4201                         _editor->clicked_axisview->order_selection_trims (_item, false);
4202                 }
4203                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4204                 break;
4205
4206         case SelectionMove:
4207                 Drag::start_grab (event, cursor);
4208                 break;
4209
4210         case SelectionExtend:
4211                 Drag::start_grab (event, cursor);
4212                 break;
4213         }
4214
4215         if (_operation == SelectionMove) {
4216                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4217         } else {
4218                 show_verbose_cursor_time (adjusted_current_frame (event));
4219         }
4220
4221         _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4222 }
4223
4224 void
4225 SelectionDrag::setup_pointer_frame_offset ()
4226 {
4227         switch (_operation) {
4228         case CreateSelection:
4229                 _pointer_frame_offset = 0;
4230                 break;
4231
4232         case SelectionStartTrim:
4233         case SelectionMove:
4234                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4235                 break;
4236
4237         case SelectionEndTrim:
4238                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4239                 break;
4240
4241         case SelectionExtend:
4242                 break;
4243         }
4244 }
4245
4246 void
4247 SelectionDrag::motion (GdkEvent* event, bool first_move)
4248 {
4249         framepos_t start = 0;
4250         framepos_t end = 0;
4251         framecnt_t length = 0;
4252         framecnt_t distance = 0;
4253
4254         framepos_t const pending_position = adjusted_current_frame (event);
4255
4256         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4257                 return;
4258         }
4259
4260         switch (_operation) {
4261         case CreateSelection:
4262         {
4263                 framepos_t grab = grab_frame ();
4264
4265                 if (first_move) {
4266                         grab = adjusted_current_frame (event, false);
4267                         if (grab < pending_position) {
4268                                 _editor->snap_to (grab, RoundDownMaybe);
4269                         }  else {
4270                                 _editor->snap_to (grab, RoundUpMaybe);
4271                         }
4272                 }
4273
4274                 if (pending_position < grab) {
4275                         start = pending_position;
4276                         end = grab;
4277                 } else {
4278                         end = pending_position;
4279                         start = grab;
4280                 }
4281
4282                 /* first drag: Either add to the selection
4283                    or create a new selection
4284                 */
4285
4286                 if (first_move) {
4287
4288                         if (_add) {
4289
4290                                 /* adding to the selection */
4291                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4292                                 _editor->clicked_selection = _editor->selection->add (start, end);
4293                                 _add = false;
4294                                 
4295                         } else {
4296
4297                                 /* new selection */
4298
4299                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4300                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4301                                 }
4302
4303                                 _editor->clicked_selection = _editor->selection->set (start, end);
4304                         }
4305                 }
4306                 
4307                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff, 
4308                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this 
4309                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4310                 if (atest) {
4311                         _editor->selection->add (atest);
4312                         break; 
4313                 }
4314                 
4315                 /* select all tracks within the rectangle that we've marked out so far */
4316                 TrackViewList new_selection;
4317                 TrackViewList& all_tracks (_editor->track_views);
4318
4319                 ArdourCanvas::Coord const top = grab_y();
4320                 ArdourCanvas::Coord const bottom = current_pointer_y();
4321
4322                 if (top >= 0 && bottom >= 0) {
4323
4324                         //first, find the tracks that are covered in the y range selection
4325                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4326                                 if ((*i)->covered_by_y_range (top, bottom)) {
4327                                         new_selection.push_back (*i);
4328                                 }
4329                         }
4330
4331                         //now find any tracks that are GROUPED with the tracks we selected
4332                         TrackViewList grouped_add = new_selection;
4333                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4334                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4335                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4336                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4337                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4338                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4339                                                         grouped_add.push_back (*j);
4340                                         }
4341                                 }
4342                         }
4343
4344                         //now compare our list with the current selection, and add or remove as necessary
4345                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4346                         TrackViewList tracks_to_add;
4347                         TrackViewList tracks_to_remove;
4348                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4349                                 if ( !_editor->selection->tracks.contains ( *i ) )
4350                                         tracks_to_add.push_back ( *i );
4351                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4352                                 if ( !grouped_add.contains ( *i ) )
4353                                         tracks_to_remove.push_back ( *i );
4354                         _editor->selection->add(tracks_to_add);
4355                         _editor->selection->remove(tracks_to_remove);
4356
4357                 }
4358         }
4359         break;
4360
4361         case SelectionStartTrim:
4362
4363                 start = _editor->selection->time[_editor->clicked_selection].start;
4364                 end = _editor->selection->time[_editor->clicked_selection].end;
4365
4366                 if (pending_position > end) {
4367                         start = end;
4368                 } else {
4369                         start = pending_position;
4370                 }
4371                 break;
4372
4373         case SelectionEndTrim:
4374
4375                 start = _editor->selection->time[_editor->clicked_selection].start;
4376                 end = _editor->selection->time[_editor->clicked_selection].end;
4377
4378                 if (pending_position < start) {
4379                         end = start;
4380                 } else {
4381                         end = pending_position;
4382                 }
4383
4384                 break;
4385                 
4386         case SelectionMove:
4387
4388                 start = _editor->selection->time[_editor->clicked_selection].start;
4389                 end = _editor->selection->time[_editor->clicked_selection].end;
4390
4391                 length = end - start;
4392                 distance = pending_position - start;
4393                 start = pending_position;
4394                 _editor->snap_to (start);
4395
4396                 end = start + length;
4397
4398                 break;
4399
4400         case SelectionExtend:
4401                 break;
4402         }
4403
4404         if (start != end) {
4405                 switch (_operation) {
4406                 case SelectionMove:     
4407                         if (_time_selection_at_start) {
4408                                 _editor->selection->move_time (distance);
4409                         }
4410                         break;
4411                 default:
4412                         _editor->selection->replace (_editor->clicked_selection, start, end);
4413                 }
4414         }
4415
4416         if (_operation == SelectionMove) {
4417                 show_verbose_cursor_time(start);
4418         } else {
4419                 show_verbose_cursor_time(pending_position);
4420         }
4421 }
4422
4423 void
4424 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4425 {
4426         Session* s = _editor->session();
4427
4428         _editor->begin_reversible_selection_op (_("Change Time Selection"));
4429         if (movement_occurred) {
4430                 motion (event, false);
4431                 /* XXX this is not object-oriented programming at all. ick */
4432                 if (_editor->selection->time.consolidate()) {
4433                         _editor->selection->TimeChanged ();
4434                 }
4435
4436                 /* XXX what if its a music time selection? */
4437                 if (s) {
4438                         if ( s->get_play_range() && s->transport_rolling() ) {
4439                                 s->request_play_range (&_editor->selection->time, true);
4440                         } else {
4441                                 if (ARDOUR_UI::config()->get_follow_edits() && !s->transport_rolling()) {
4442                                         if (_operation == SelectionEndTrim)
4443                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4444                                         else
4445                                                 s->request_locate (_editor->get_selection().time.start());
4446                                 }
4447                         }
4448                 }
4449
4450         } else {
4451                 /* just a click, no pointer movement.
4452                  */
4453
4454                 if (_operation == SelectionExtend) {
4455                         if (_time_selection_at_start) {
4456                                 framepos_t pos = adjusted_current_frame (event, false);
4457                                 framepos_t start = min (pos, start_at_start);
4458                                 framepos_t end = max (pos, end_at_start);
4459                                 _editor->selection->set (start, end);
4460                         }
4461                 } else {
4462                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4463                                 if (_editor->clicked_selection) {
4464                                         _editor->selection->remove (_editor->clicked_selection);
4465                                 }
4466                         } else {
4467                                 if (!_editor->clicked_selection) {
4468                                         _editor->selection->clear_time();
4469                                 }
4470                         }
4471                 }
4472
4473                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4474                         _editor->selection->set (_editor->clicked_axisview);
4475                 }
4476                         
4477                 if (s && s->get_play_range () && s->transport_rolling()) {
4478                         s->request_stop (false, false);
4479                 }
4480
4481         }
4482
4483         _editor->stop_canvas_autoscroll ();
4484         _editor->clicked_selection = 0;
4485         _editor->commit_reversible_selection_op ();
4486 }
4487
4488 void
4489 SelectionDrag::aborted (bool)
4490 {
4491         /* XXX: TODO */
4492 }
4493
4494 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4495         : Drag (e, i, false),
4496           _operation (o),
4497           _copy (false)
4498 {
4499         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4500
4501         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group, 
4502                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
4503                                                                       physical_screen_height (_editor->get_window())));
4504         _drag_rect->hide ();
4505
4506         _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4507         _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4508 }
4509
4510 void
4511 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4512 {
4513         if (_editor->session() == 0) {
4514                 return;
4515         }
4516
4517         Gdk::Cursor* cursor = MouseCursors::invalid_cursor();
4518
4519         if (!_editor->temp_location) {
4520                 _editor->temp_location = new Location (*_editor->session());
4521         }
4522
4523         switch (_operation) {
4524         case CreateSkipMarker:
4525         case CreateRangeMarker:
4526         case CreateTransportMarker:
4527         case CreateCDMarker:
4528
4529                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4530                         _copy = true;
4531                 } else {
4532                         _copy = false;
4533                 }
4534                 cursor = _editor->cursors()->selector;
4535                 break;
4536         }
4537
4538         Drag::start_grab (event, cursor);
4539
4540         show_verbose_cursor_time (adjusted_current_frame (event));
4541 }
4542
4543 void
4544 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4545 {
4546         framepos_t start = 0;
4547         framepos_t end = 0;
4548         ArdourCanvas::Rectangle *crect;
4549
4550         switch (_operation) {
4551         case CreateSkipMarker:
4552                 crect = _editor->range_bar_drag_rect;
4553                 break;
4554         case CreateRangeMarker:
4555                 crect = _editor->range_bar_drag_rect;
4556                 break;
4557         case CreateTransportMarker:
4558                 crect = _editor->transport_bar_drag_rect;
4559                 break;
4560         case CreateCDMarker:
4561                 crect = _editor->cd_marker_bar_drag_rect;
4562                 break;
4563         default:
4564                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4565                 return;
4566                 break;
4567         }
4568
4569         framepos_t const pf = adjusted_current_frame (event);
4570
4571         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4572                 framepos_t grab = grab_frame ();
4573                 _editor->snap_to (grab);
4574
4575                 if (pf < grab_frame()) {
4576                         start = pf;
4577                         end = grab;
4578                 } else {
4579                         end = pf;
4580                         start = grab;
4581                 }
4582
4583                 /* first drag: Either add to the selection
4584                    or create a new selection.
4585                 */
4586
4587                 if (first_move) {
4588
4589                         _editor->temp_location->set (start, end);
4590
4591                         crect->show ();
4592
4593                         update_item (_editor->temp_location);
4594                         _drag_rect->show();
4595                         //_drag_rect->raise_to_top();
4596
4597                 }
4598         }
4599
4600         if (start != end) {
4601                 _editor->temp_location->set (start, end);
4602
4603                 double x1 = _editor->sample_to_pixel (start);
4604                 double x2 = _editor->sample_to_pixel (end);
4605                 crect->set_x0 (x1);
4606                 crect->set_x1 (x2);
4607
4608                 update_item (_editor->temp_location);
4609         }
4610
4611         show_verbose_cursor_time (pf);
4612
4613 }
4614
4615 void
4616 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4617 {
4618         Location * newloc = 0;
4619         string rangename;
4620         int flags;
4621
4622         if (movement_occurred) {
4623                 motion (event, false);
4624                 _drag_rect->hide();
4625
4626                 switch (_operation) {
4627                 case CreateSkipMarker:
4628                 case CreateRangeMarker:
4629                 case CreateCDMarker:
4630                     {
4631                         XMLNode &before = _editor->session()->locations()->get_state();
4632                         if (_operation == CreateSkipMarker) {
4633                                 _editor->begin_reversible_command (_("new skip marker"));
4634                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4635                                 flags = Location::IsRangeMarker | Location::IsSkip;
4636                                 _editor->range_bar_drag_rect->hide();
4637                         } else if (_operation == CreateCDMarker) {
4638                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4639                                 _editor->begin_reversible_command (_("new CD marker"));
4640                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
4641                                 _editor->cd_marker_bar_drag_rect->hide();
4642                         } else {
4643                                 _editor->begin_reversible_command (_("new skip marker"));
4644                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4645                                 flags = Location::IsRangeMarker;
4646                                 _editor->range_bar_drag_rect->hide();
4647                         }
4648                         newloc = new Location (
4649                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4650                                 );
4651
4652                         _editor->session()->locations()->add (newloc, true);
4653                         XMLNode &after = _editor->session()->locations()->get_state();
4654                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4655                         _editor->commit_reversible_command ();
4656                         break;
4657                     }
4658
4659                 case CreateTransportMarker:
4660                         // popup menu to pick loop or punch
4661                         _editor->new_transport_marker_context_menu (&event->button, _item);
4662                         break;
4663                 }
4664
4665         } else {
4666
4667                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4668
4669                 if (_operation == CreateTransportMarker) {
4670
4671                         /* didn't drag, so just locate */
4672
4673                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4674
4675                 } else if (_operation == CreateCDMarker) {
4676
4677                         /* didn't drag, but mark is already created so do
4678                          * nothing */
4679
4680                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4681                         
4682
4683                         framepos_t start;
4684                         framepos_t end;
4685
4686                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4687
4688                         if (end == max_framepos) {
4689                                 end = _editor->session()->current_end_frame ();
4690                         }
4691
4692                         if (start == max_framepos) {
4693                                 start = _editor->session()->current_start_frame ();
4694                         }
4695
4696                         switch (_editor->mouse_mode) {
4697                         case MouseObject:
4698                                 /* find the two markers on either side and then make the selection from it */
4699                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4700                                 break;
4701
4702                         case MouseRange:
4703                                 /* find the two markers on either side of the click and make the range out of it */
4704                                 _editor->selection->set (start, end);
4705                                 break;
4706
4707                         default:
4708                                 break;
4709                         }
4710                 }
4711         }
4712
4713         _editor->stop_canvas_autoscroll ();
4714 }
4715
4716 void
4717 RangeMarkerBarDrag::aborted (bool)
4718 {
4719         /* XXX: TODO */
4720 }
4721
4722 void
4723 RangeMarkerBarDrag::update_item (Location* location)
4724 {
4725         double const x1 = _editor->sample_to_pixel (location->start());
4726         double const x2 = _editor->sample_to_pixel (location->end());
4727
4728         _drag_rect->set_x0 (x1);
4729         _drag_rect->set_x1 (x2);
4730 }
4731
4732 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4733         : Drag (e, i)
4734         , _cumulative_dx (0)
4735         , _cumulative_dy (0)
4736 {
4737         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4738
4739         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4740         assert (_primary);
4741         _region = &_primary->region_view ();
4742         _note_height = _region->midi_stream_view()->note_height ();
4743 }
4744
4745 void
4746 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4747 {
4748         Drag::start_grab (event);
4749
4750         if (!(_was_selected = _primary->selected())) {
4751
4752                 /* tertiary-click means extend selection - we'll do that on button release,
4753                    so don't add it here, because otherwise we make it hard to figure
4754                    out the "extend-to" range.
4755                 */
4756
4757                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4758
4759                 if (!extend) {
4760                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4761
4762                         if (add) {
4763                                 _region->note_selected (_primary, true);
4764                         } else {
4765                                 _region->unique_select (_primary);
4766                         }
4767
4768                         _editor->begin_reversible_selection_op(_("Select Note Press"));
4769                         _editor->commit_reversible_selection_op();
4770                 }
4771         }
4772 }
4773
4774 /** @return Current total drag x change in frames */
4775 frameoffset_t
4776 NoteDrag::total_dx () const
4777 {
4778         /* dx in frames */
4779         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4780
4781         /* primary note time */
4782         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4783
4784         /* new time of the primary note in session frames */
4785         frameoffset_t st = n + dx;
4786
4787         framepos_t const rp = _region->region()->position ();
4788
4789         /* prevent the note being dragged earlier than the region's position */
4790         st = max (st, rp);
4791
4792         /* snap and return corresponding delta */
4793         return _region->snap_frame_to_frame (st - rp) + rp - n;
4794 }
4795
4796 /** @return Current total drag y change in note number */
4797 int8_t
4798 NoteDrag::total_dy () const
4799 {
4800         MidiStreamView* msv = _region->midi_stream_view ();
4801         double const y = _region->midi_view()->y_position ();
4802         /* new current note */
4803         uint8_t n = msv->y_to_note (current_pointer_y () - y);
4804         /* clamp */
4805         n = max (msv->lowest_note(), n);
4806         n = min (msv->highest_note(), n);
4807         /* and work out delta */
4808         return n - msv->y_to_note (grab_y() - y);
4809 }
4810
4811 void
4812 NoteDrag::motion (GdkEvent *, bool)
4813 {
4814         /* Total change in x and y since the start of the drag */
4815         frameoffset_t const dx = total_dx ();
4816         int8_t const dy = total_dy ();
4817
4818         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4819         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4820         double const tdy = -dy * _note_height - _cumulative_dy;
4821
4822         if (tdx || tdy) {
4823                 _cumulative_dx += tdx;
4824                 _cumulative_dy += tdy;
4825
4826                 int8_t note_delta = total_dy();
4827
4828                 _region->move_selection (tdx, tdy, note_delta);
4829
4830                 /* the new note value may be the same as the old one, but we
4831                  * don't know what that means because the selection may have
4832                  * involved more than one note and we might be doing something
4833                  * odd with them. so show the note value anyway, always.
4834                  */
4835
4836                 char buf[12];
4837                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4838                 
4839                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4840                           (int) floor ((double)new_note));
4841
4842                 show_verbose_cursor_text (buf);
4843         }
4844 }
4845
4846 void
4847 NoteDrag::finished (GdkEvent* ev, bool moved)
4848 {
4849         if (!moved) {
4850                 /* no motion - select note */
4851
4852                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4853                     _editor->current_mouse_mode() == Editing::MouseDraw) {
4854
4855                         bool changed = false;
4856
4857                         if (_was_selected) {
4858                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4859                                 if (add) {
4860                                         _region->note_deselected (_primary);
4861                                         changed = true;
4862                                 }
4863                         } else {
4864                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4865                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4866
4867                                 if (!extend && !add && _region->selection_size() > 1) {
4868                                         _region->unique_select (_primary);
4869                                         changed = true;
4870                                 } else if (extend) {
4871                                         _region->note_selected (_primary, true, true);
4872                                         changed = true;
4873                                 } else {
4874                                         /* it was added during button press */
4875                                 }
4876                         }
4877
4878                         if (changed) {
4879                                 _editor->begin_reversible_selection_op(_("Select Note Release"));
4880                                 _editor->commit_reversible_selection_op();
4881                         }
4882                 }
4883         } else {
4884                 _region->note_dropped (_primary, total_dx(), total_dy());
4885         }
4886 }
4887
4888 void
4889 NoteDrag::aborted (bool)
4890 {
4891         /* XXX: TODO */
4892 }
4893
4894 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4895 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4896         : Drag (editor, atv->base_item ())
4897         , _ranges (r)
4898         , _y_origin (atv->y_position())
4899         , _nothing_to_drag (false)
4900 {
4901         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4902         setup (atv->lines ());
4903 }
4904
4905 /** Make an AutomationRangeDrag for region gain lines or MIDI controller regions */
4906 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, RegionView* rv, list<AudioRange> const & r)
4907         : Drag (editor, rv->get_canvas_group ())
4908         , _ranges (r)
4909         , _y_origin (rv->get_time_axis_view().y_position())
4910         , _nothing_to_drag (false)
4911         , _integral (false)
4912 {
4913         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4914
4915         list<boost::shared_ptr<AutomationLine> > lines;
4916
4917         AudioRegionView*      audio_view;
4918         AutomationRegionView* automation_view;
4919         if ((audio_view = dynamic_cast<AudioRegionView*>(rv))) {
4920                 lines.push_back (audio_view->get_gain_line ());
4921         } else if ((automation_view = dynamic_cast<AutomationRegionView*>(rv))) {
4922                 lines.push_back (automation_view->line ());
4923                 _integral = true;
4924         } else {
4925                 error << _("Automation range drag created for invalid region type") << endmsg;
4926         }
4927
4928         setup (lines);
4929 }
4930
4931 /** @param lines AutomationLines to drag.
4932  *  @param offset Offset from the session start to the points in the AutomationLines.
4933  */
4934 void
4935 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4936 {
4937         /* find the lines that overlap the ranges being dragged */
4938         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4939         while (i != lines.end ()) {
4940                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4941                 ++j;
4942
4943                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4944
4945                 /* check this range against all the AudioRanges that we are using */
4946                 list<AudioRange>::const_iterator k = _ranges.begin ();
4947                 while (k != _ranges.end()) {
4948                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4949                                 break;
4950                         }
4951                         ++k;
4952                 }
4953
4954                 /* add it to our list if it overlaps at all */
4955                 if (k != _ranges.end()) {
4956                         Line n;
4957                         n.line = *i;
4958                         n.state = 0;
4959                         n.range = r;
4960                         _lines.push_back (n);
4961                 }
4962
4963                 i = j;
4964         }
4965
4966         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4967 }
4968
4969 double
4970 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4971 {
4972         return 1.0 - ((global_y - _y_origin) / line->height());
4973 }
4974
4975 double
4976 AutomationRangeDrag::value (boost::shared_ptr<AutomationList> list, double x) const
4977 {
4978         const double v = list->eval(x);
4979         return _integral ? rint(v) : v;
4980 }
4981
4982 void
4983 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4984 {
4985         Drag::start_grab (event, cursor);
4986
4987         /* Get line states before we start changing things */
4988         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4989                 i->state = &i->line->get_state ();
4990                 i->original_fraction = y_fraction (i->line, current_pointer_y());
4991         }
4992
4993         if (_ranges.empty()) {
4994
4995                 /* No selected time ranges: drag all points */
4996                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4997                         uint32_t const N = i->line->npoints ();
4998                         for (uint32_t j = 0; j < N; ++j) {
4999                                 i->points.push_back (i->line->nth (j));
5000                         }
5001                 }
5002
5003         } else {
5004
5005                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
5006
5007                         framecnt_t const half = (i->start + i->end) / 2;
5008
5009                         /* find the line that this audio range starts in */
5010                         list<Line>::iterator j = _lines.begin();
5011                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
5012                                 ++j;
5013                         }
5014
5015                         if (j != _lines.end()) {
5016                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5017
5018                                 /* j is the line that this audio range starts in; fade into it;
5019                                    64 samples length plucked out of thin air.
5020                                 */
5021
5022                                 framepos_t a = i->start + 64;
5023                                 if (a > half) {
5024                                         a = half;
5025                                 }
5026
5027                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
5028                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
5029
5030                                 the_list->editor_add (p, value (the_list, p));
5031                                 the_list->editor_add (q, value (the_list, q));
5032                         }
5033
5034                         /* same thing for the end */
5035
5036                         j = _lines.begin();
5037                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
5038                                 ++j;
5039                         }
5040
5041                         if (j != _lines.end()) {
5042                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
5043
5044                                 /* j is the line that this audio range starts in; fade out of it;
5045                                    64 samples length plucked out of thin air.
5046                                 */
5047
5048                                 framepos_t b = i->end - 64;
5049                                 if (b < half) {
5050                                         b = half;
5051                                 }
5052
5053                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
5054                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
5055
5056                                 the_list->editor_add (p, value (the_list, p));
5057                                 the_list->editor_add (q, value (the_list, q));
5058                         }
5059                 }
5060
5061                 _nothing_to_drag = true;
5062
5063                 /* Find all the points that should be dragged and put them in the relevant
5064                    points lists in the Line structs.
5065                 */
5066
5067                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5068
5069                         uint32_t const N = i->line->npoints ();
5070                         for (uint32_t j = 0; j < N; ++j) {
5071
5072                                 /* here's a control point on this line */
5073                                 ControlPoint* p = i->line->nth (j);
5074                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
5075
5076                                 /* see if it's inside a range */
5077                                 list<AudioRange>::const_iterator k = _ranges.begin ();
5078                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5079                                         ++k;
5080                                 }
5081
5082                                 if (k != _ranges.end()) {
5083                                         /* dragging this point */
5084                                         _nothing_to_drag = false;
5085                                         i->points.push_back (p);
5086                                 }
5087                         }
5088                 }
5089         }
5090
5091         if (_nothing_to_drag) {
5092                 return;
5093         }
5094
5095         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5096                 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5097         }
5098 }
5099
5100 void
5101 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5102 {
5103         if (_nothing_to_drag) {
5104                 return;
5105         }
5106
5107         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5108                 float const f = y_fraction (l->line, current_pointer_y());
5109                 /* we are ignoring x position for this drag, so we can just pass in anything */
5110                 uint32_t ignored;
5111                 l->line->drag_motion (0, f, true, false, ignored);
5112                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5113         }
5114 }
5115
5116 void
5117 AutomationRangeDrag::finished (GdkEvent* event, bool)
5118 {
5119         if (_nothing_to_drag) {
5120                 return;
5121         }
5122
5123         motion (event, false);
5124         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5125                 i->line->end_drag (false, 0);
5126         }
5127
5128         _editor->commit_reversible_command ();
5129 }
5130
5131 void
5132 AutomationRangeDrag::aborted (bool)
5133 {
5134         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5135                 i->line->reset ();
5136         }
5137 }
5138
5139 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5140         : view (v)
5141         , initial_time_axis_view (itav)
5142 {
5143         /* note that time_axis_view may be null if the regionview was created
5144          * as part of a copy operation.
5145          */
5146         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5147         layer = v->region()->layer ();
5148         initial_y = v->get_canvas_group()->position().y;
5149         initial_playlist = v->region()->playlist ();
5150         initial_position = v->region()->position ();
5151         initial_end = v->region()->position () + v->region()->length ();
5152 }
5153
5154 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5155         : Drag (e, i->canvas_item ())
5156         , _region_view (r)
5157         , _patch_change (i)
5158         , _cumulative_dx (0)
5159 {
5160         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5161                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5162                                                    grab_frame()));
5163 }
5164
5165 void
5166 PatchChangeDrag::motion (GdkEvent* ev, bool)
5167 {
5168         framepos_t f = adjusted_current_frame (ev);
5169         boost::shared_ptr<Region> r = _region_view->region ();
5170         f = max (f, r->position ());
5171         f = min (f, r->last_frame ());
5172
5173         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5174         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5175         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5176         _cumulative_dx = dxu;
5177 }
5178
5179 void
5180 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5181 {
5182         if (!movement_occurred) {
5183                 return;
5184         }
5185
5186         boost::shared_ptr<Region> r (_region_view->region ());
5187         framepos_t f = adjusted_current_frame (ev);
5188         f = max (f, r->position ());
5189         f = min (f, r->last_frame ());
5190
5191         _region_view->move_patch_change (
5192                 *_patch_change,
5193                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5194                 );
5195 }
5196
5197 void
5198 PatchChangeDrag::aborted (bool)
5199 {
5200         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5201 }
5202
5203 void
5204 PatchChangeDrag::setup_pointer_frame_offset ()
5205 {
5206         boost::shared_ptr<Region> region = _region_view->region ();
5207         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5208 }
5209
5210 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5211         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5212         , _region_view (rv)
5213 {
5214
5215 }
5216
5217 void
5218 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5219 {
5220         _region_view->update_drag_selection (
5221                 x1, x2, y1, y2,
5222                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5223 }
5224
5225 void
5226 MidiRubberbandSelectDrag::deselect_things ()
5227 {
5228         /* XXX */
5229 }
5230
5231 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5232         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5233         , _region_view (rv)
5234 {
5235         _vertical_only = true;
5236 }
5237
5238 void
5239 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5240 {
5241         double const y = _region_view->midi_view()->y_position ();
5242
5243         y1 = max (0.0, y1 - y);
5244         y2 = max (0.0, y2 - y);
5245         
5246         _region_view->update_vertical_drag_selection (
5247                 y1,
5248                 y2,
5249                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5250                 );
5251 }
5252
5253 void
5254 MidiVerticalSelectDrag::deselect_things ()
5255 {
5256         /* XXX */
5257 }
5258
5259 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5260         : RubberbandSelectDrag (e, i)
5261 {
5262
5263 }
5264
5265 void
5266 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5267 {
5268         if (drag_in_progress) {
5269                 /* We just want to select things at the end of the drag, not during it */
5270                 return;
5271         }
5272         
5273         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5274
5275         _editor->begin_reversible_selection_op (_("rubberband selection"));
5276
5277         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5278
5279         _editor->commit_reversible_selection_op ();
5280 }
5281
5282 void
5283 EditorRubberbandSelectDrag::deselect_things ()
5284 {
5285         _editor->begin_reversible_selection_op (_("Clear Selection (rubberband)"));
5286
5287         _editor->selection->clear_tracks();
5288         _editor->selection->clear_regions();
5289         _editor->selection->clear_points ();
5290         _editor->selection->clear_lines ();
5291         _editor->selection->clear_midi_notes ();
5292
5293         _editor->commit_reversible_selection_op();
5294 }
5295
5296 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5297         : Drag (e, i)
5298         , _region_view (rv)
5299         , _drag_rect (0)
5300 {
5301         _note[0] = _note[1] = 0;
5302 }
5303
5304 NoteCreateDrag::~NoteCreateDrag ()
5305 {
5306         delete _drag_rect;
5307 }
5308
5309 framecnt_t
5310 NoteCreateDrag::grid_frames (framepos_t t) const
5311 {
5312         bool success;
5313         Evoral::Beats grid_beats = _editor->get_grid_type_as_beats (success, t);
5314         if (!success) {
5315                 grid_beats = Evoral::Beats(1);
5316         }
5317
5318         return _region_view->region_beats_to_region_frames (grid_beats);
5319 }
5320
5321 void
5322 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5323 {
5324         Drag::start_grab (event, cursor);
5325                                                  
5326         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5327
5328         framepos_t pf = _drags->current_pointer_frame ();
5329         framecnt_t const g = grid_frames (pf);
5330
5331         /* Hack so that we always snap to the note that we are over, instead of snapping
5332            to the next one if we're more than halfway through the one we're over.
5333         */
5334         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5335                 pf -= g / 2;
5336         }
5337
5338         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5339         _note[1] = _note[0];
5340
5341         MidiStreamView* sv = _region_view->midi_stream_view ();
5342         double const x = _editor->sample_to_pixel (_note[0]);
5343         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5344
5345         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5346         _drag_rect->set_outline_all ();
5347         _drag_rect->set_outline_color (0xffffff99);
5348         _drag_rect->set_fill_color (0xffffff66);
5349 }
5350
5351 void
5352 NoteCreateDrag::motion (GdkEvent* event, bool)
5353 {
5354         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5355         double const x0 = _editor->sample_to_pixel (_note[0]);
5356         double const x1 = _editor->sample_to_pixel (_note[1]);
5357         _drag_rect->set_x0 (std::min(x0, x1));
5358         _drag_rect->set_x1 (std::max(x0, x1));
5359 }
5360
5361 void
5362 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5363 {
5364         if (!had_movement) {
5365                 return;
5366         }
5367         
5368         framepos_t const start = min (_note[0], _note[1]);
5369         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5370
5371         framecnt_t const g = grid_frames (start);
5372         Evoral::Beats const one_tick = Evoral::Beats::ticks(1);
5373         
5374         if (_editor->snap_mode() == SnapNormal && length < g) {
5375                 length = g;
5376         }
5377
5378         Evoral::Beats length_beats = max (
5379                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5380
5381         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5382 }
5383
5384 double
5385 NoteCreateDrag::y_to_region (double y) const
5386 {
5387         double x = 0;
5388         _region_view->get_canvas_group()->canvas_to_item (x, y);
5389         return y;
5390 }
5391
5392 void
5393 NoteCreateDrag::aborted (bool)
5394 {
5395         
5396 }
5397
5398 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5399         : Drag (e, i)
5400         , arv (rv)
5401         , start (start_yn)
5402 {
5403         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5404 }
5405
5406 void
5407 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5408 {
5409         Drag::start_grab (event, cursor);
5410 }
5411
5412 void
5413 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5414 {
5415         double distance;
5416         double new_length;
5417         framecnt_t len;
5418
5419         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5420
5421         if (start) {
5422                 distance = _drags->current_pointer_x() - grab_x();
5423                 len = ar->fade_in()->back()->when;
5424         } else {
5425                 distance = grab_x() - _drags->current_pointer_x();
5426                 len = ar->fade_out()->back()->when;
5427         }
5428
5429         /* how long should it be ? */
5430
5431         new_length = len + _editor->pixel_to_sample (distance);
5432
5433         /* now check with the region that this is legal */
5434
5435         new_length = ar->verify_xfade_bounds (new_length, start);
5436
5437         if (start) {
5438                 arv->reset_fade_in_shape_width (ar, new_length);
5439         } else {
5440                 arv->reset_fade_out_shape_width (ar, new_length);
5441         }
5442 }
5443
5444 void
5445 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5446 {
5447         double distance;
5448         double new_length;
5449         framecnt_t len;
5450
5451         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5452
5453         if (start) {
5454                 distance = _drags->current_pointer_x() - grab_x();
5455                 len = ar->fade_in()->back()->when;
5456         } else {
5457                 distance = grab_x() - _drags->current_pointer_x();
5458                 len = ar->fade_out()->back()->when;
5459         }
5460
5461         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5462         
5463         _editor->begin_reversible_command ("xfade trim");
5464         ar->playlist()->clear_owned_changes (); 
5465
5466         if (start) {
5467                 ar->set_fade_in_length (new_length);
5468         } else {
5469                 ar->set_fade_out_length (new_length);
5470         }
5471
5472         /* Adjusting the xfade may affect other regions in the playlist, so we need
5473            to get undo Commands from the whole playlist rather than just the
5474            region.
5475         */
5476
5477         vector<Command*> cmds;
5478         ar->playlist()->rdiff (cmds);
5479         _editor->session()->add_commands (cmds);
5480         _editor->commit_reversible_command ();
5481
5482 }
5483
5484 void
5485 CrossfadeEdgeDrag::aborted (bool)
5486 {
5487         if (start) {
5488                 arv->redraw_start_xfade ();
5489         } else {
5490                 arv->redraw_end_xfade ();
5491         }
5492 }
5493
5494 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5495         : Drag (e, item, true)
5496         , line (new EditorCursor (*e))
5497 {
5498         line->set_position (pos);
5499         line->show ();
5500 }
5501
5502 RegionCutDrag::~RegionCutDrag ()
5503 {
5504         delete line;
5505 }
5506
5507 void
5508 RegionCutDrag::motion (GdkEvent*, bool)
5509 {
5510         framepos_t where = _drags->current_pointer_frame();
5511         _editor->snap_to (where);
5512
5513         line->set_position (where);
5514 }
5515
5516 void
5517 RegionCutDrag::finished (GdkEvent*, bool)
5518 {
5519         _editor->get_track_canvas()->canvas()->re_enter();
5520
5521         framepos_t pos = _drags->current_pointer_frame();
5522         
5523         line->hide ();
5524
5525         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5526
5527         if (rs.empty()) {
5528                 return;
5529         }
5530
5531         _editor->split_regions_at (pos, rs);
5532 }
5533
5534 void
5535 RegionCutDrag::aborted (bool)
5536 {
5537 }