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