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