Add "maybe" rounding modes for rounding only if necessary.
[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                 
1795                 /* just a click */
1796
1797                 if (was_double_click() && !_views.empty()) {
1798                         DraggingView dv = _views.front();
1799                         dv.view->show_region_editor ();
1800                         
1801                 }
1802
1803                 return;
1804         }
1805
1806         _editor->begin_reversible_command(_("Ripple drag"));
1807         
1808         // remove the regions being rippled from the dragging view, updating them to 
1809         // their new positions
1810         remove_unselected_from_views (prev_amount, true);
1811
1812         if (allow_moves_across_tracks) {
1813                 if (orig_tav) {
1814                         // if regions were dragged across tracks, we've rippled any later
1815                         // regions on the track the regions were dragged off, so we need
1816                         // to add the original track to the undo record
1817                         orig_tav->playlist()->clear_changes();
1818                         vector<Command*> cmds;
1819                         orig_tav->playlist()->rdiff (cmds);
1820                         _editor->session()->add_commands (cmds);
1821                 }
1822                 if (prev_tav && prev_tav != orig_tav) {
1823                         prev_tav->playlist()->clear_changes();
1824                         vector<Command*> cmds;
1825                         prev_tav->playlist()->rdiff (cmds);
1826                         _editor->session()->add_commands (cmds);
1827                 }
1828         } else {
1829                 // selection spanned multiple tracks - all will need adding to undo record
1830
1831                 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1832                 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1833
1834                 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1835                         (*pi)->clear_changes();
1836                         vector<Command*> cmds;
1837                         (*pi)->rdiff (cmds);
1838                         _editor->session()->add_commands (cmds);
1839                 }
1840         }
1841
1842         // other modified playlists are added to undo by RegionMoveDrag::finished()
1843         RegionMoveDrag::finished (event, movement_occurred);
1844         _editor->commit_reversible_command();
1845 }
1846
1847 void
1848 RegionRippleDrag::aborted (bool movement_occurred)
1849 {
1850         RegionMoveDrag::aborted (movement_occurred);
1851         _views.clear ();
1852 }
1853
1854
1855 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1856         : Drag (e, i),
1857           _view (dynamic_cast<MidiTimeAxisView*> (v))
1858 {
1859         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1860
1861         assert (_view);
1862 }
1863
1864 void
1865 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1866 {
1867         if (first_move) {
1868                 _region = add_midi_region (_view);
1869                 _view->playlist()->freeze ();
1870         } else {
1871                 if (_region) {
1872                         framepos_t const f = adjusted_current_frame (event);
1873                         if (f < grab_frame()) {
1874                                 _region->set_position (f);
1875                         }
1876
1877                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1878                            so that if this region is duplicated, its duplicate starts on
1879                            a snap point rather than 1 frame after a snap point.  Otherwise things get
1880                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
1881                            place snapped notes at the start of the region.
1882                         */
1883
1884                         framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1885                         _region->set_length (len < 1 ? 1 : len);
1886                 }
1887         }
1888 }
1889
1890 void
1891 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1892 {
1893         if (!movement_occurred) {
1894                 add_midi_region (_view);
1895         } else {
1896                 _view->playlist()->thaw ();
1897         }
1898 }
1899
1900 void
1901 RegionCreateDrag::aborted (bool)
1902 {
1903         if (_region) {
1904                 _view->playlist()->thaw ();
1905         }
1906
1907         /* XXX */
1908 }
1909
1910 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1911         : Drag (e, i)
1912         , region (0)
1913 {
1914         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1915 }
1916
1917 void
1918 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1919 {
1920         Gdk::Cursor* cursor;
1921         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1922         assert (cnote);
1923         float x_fraction = cnote->mouse_x_fraction ();
1924
1925         if (x_fraction > 0.0 && x_fraction < 0.25) {
1926                 cursor = _editor->cursors()->left_side_trim;
1927         } else  {
1928                 cursor = _editor->cursors()->right_side_trim;
1929         }
1930
1931         Drag::start_grab (event, cursor);
1932
1933         region = &cnote->region_view();
1934
1935         double const region_start = region->get_position_pixels();
1936         double const middle_point = region_start + cnote->x0() + (cnote->x1() - cnote->x0()) / 2.0L;
1937
1938         if (grab_x() <= middle_point) {
1939                 cursor = _editor->cursors()->left_side_trim;
1940                 at_front = true;
1941         } else {
1942                 cursor = _editor->cursors()->right_side_trim;
1943                 at_front = false;
1944         }
1945
1946         _item->grab ();
1947
1948         if (event->motion.state & Keyboard::PrimaryModifier) {
1949                 relative = false;
1950         } else {
1951                 relative = true;
1952         }
1953
1954         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1955
1956         if (ms.size() > 1) {
1957                 /* has to be relative, may make no sense otherwise */
1958                 relative = true;
1959         }
1960
1961         /* select this note; if it is already selected, preserve the existing selection,
1962            otherwise make this note the only one selected.
1963         */
1964         region->note_selected (cnote, cnote->selected ());
1965
1966         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1967                 MidiRegionSelection::iterator next;
1968                 next = r;
1969                 ++next;
1970                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1971                 if (mrv) {
1972                         mrv->begin_resizing (at_front);
1973                 }
1974                 r = next;
1975         }
1976 }
1977
1978 void
1979 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1980 {
1981         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1982         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1983                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1984                 assert (nb);
1985                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1986                 if (mrv) {
1987                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1988                 }
1989         }
1990 }
1991
1992 void
1993 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1994 {
1995         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1996         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1997                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1998                 assert (nb);
1999                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2000                 if (mrv) {
2001                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2002                 }
2003         }
2004 }
2005
2006 void
2007 NoteResizeDrag::aborted (bool)
2008 {
2009         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2010         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2011                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2012                 if (mrv) {
2013                         mrv->abort_resizing ();
2014                 }
2015         }
2016 }
2017
2018 AVDraggingView::AVDraggingView (RegionView* v)
2019         : view (v)
2020 {
2021         initial_position = v->region()->position ();
2022 }
2023
2024 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2025         : Drag (e, i)
2026 {
2027         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2028
2029         RegionSelection rs;
2030         TrackViewList empty;
2031         empty.clear();
2032         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2033         std::list<RegionView*> views = rs.by_layer();
2034
2035         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2036                 RegionView* rv = (*i);
2037                 if (!rv->region()->video_locked()) {
2038                         continue;
2039                 }
2040                 _views.push_back (AVDraggingView (rv));
2041         }
2042 }
2043
2044 void
2045 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2046 {
2047         Drag::start_grab (event);
2048         if (_editor->session() == 0) {
2049                 return;
2050         }
2051
2052         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2053         _max_backwards_drag = (
2054                           ARDOUR_UI::instance()->video_timeline->get_duration()
2055                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2056                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2057                         );
2058
2059         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2060                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2061                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2062                 }
2063         }
2064         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2065
2066         char buf[128];
2067         Timecode::Time timecode;
2068         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2069         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);
2070         show_verbose_cursor_text (buf);
2071 }
2072
2073 void
2074 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2075 {
2076         if (_editor->session() == 0) {
2077                 return;
2078         }
2079         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2080                 return;
2081         }
2082
2083         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2084         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2085
2086         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2087                 dt = - _max_backwards_drag;
2088         }
2089
2090         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2091         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2092
2093         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2094                 RegionView* rv = i->view;
2095                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2096                 if (first_move) {
2097                         rv->drag_start ();
2098                         rv->region()->clear_changes ();
2099                         rv->region()->suspend_property_changes();
2100                 }
2101                 rv->region()->set_position(i->initial_position + dt);
2102                 rv->region_changed(ARDOUR::Properties::position);
2103         }
2104
2105         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2106         Timecode::Time timecode;
2107         Timecode::Time timediff;
2108         char buf[128];
2109         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2110         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2111         snprintf (buf, sizeof (buf),
2112                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2113                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2114                         , _("Video Start:"),
2115                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2116                         , _("Diff:"),
2117                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2118                                 );
2119         show_verbose_cursor_text (buf);
2120 }
2121
2122 void
2123 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2124 {
2125         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2126                 return;
2127         }
2128
2129         if (!movement_occurred || ! _editor->session()) {
2130                 return;
2131         }
2132
2133         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2134
2135         _editor->begin_reversible_command (_("Move Video"));
2136
2137         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2138         ARDOUR_UI::instance()->video_timeline->save_undo();
2139         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2140         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2141
2142         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2143                 i->view->drag_end();
2144                 i->view->region()->resume_property_changes ();
2145
2146                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2147         }
2148
2149         _editor->session()->maybe_update_session_range(
2150                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2151                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2152                         );
2153
2154
2155         _editor->commit_reversible_command ();
2156 }
2157
2158 void
2159 VideoTimeLineDrag::aborted (bool)
2160 {
2161         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2162                 return;
2163         }
2164         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2165         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2166
2167         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2168                 i->view->region()->resume_property_changes ();
2169                 i->view->region()->set_position(i->initial_position);
2170         }
2171 }
2172
2173 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2174         : RegionDrag (e, i, p, v)
2175         , _preserve_fade_anchor (preserve_fade_anchor)
2176         , _jump_position_when_done (false)
2177 {
2178         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2179 }
2180
2181 void
2182 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2183 {
2184         double speed = 1.0;
2185         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2186         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2187
2188         if (tv && tv->is_track()) {
2189                 speed = tv->track()->speed();
2190         }
2191
2192         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2193         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2194         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2195
2196         framepos_t const pf = adjusted_current_frame (event);
2197
2198         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2199                 /* Move the contents of the region around without changing the region bounds */
2200                 _operation = ContentsTrim;
2201                 Drag::start_grab (event, _editor->cursors()->trimmer);
2202         } else {
2203                 /* These will get overridden for a point trim.*/
2204                 if (pf < (region_start + region_length/2)) {
2205                         /* closer to front */
2206                         _operation = StartTrim;
2207
2208                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2209                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2210                         } else {
2211                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2212                         }
2213                 } else {
2214                         /* closer to end */
2215                         _operation = EndTrim;
2216                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2217                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2218                         } else {
2219                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2220                         }
2221                 }
2222         }
2223
2224         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2225                 _jump_position_when_done = true;
2226         }
2227
2228         switch (_operation) {
2229         case StartTrim:
2230                 show_verbose_cursor_time (region_start);
2231                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2232                         i->view->trim_front_starting ();
2233                 }
2234                 break;
2235         case EndTrim:
2236                 show_verbose_cursor_time (region_end);
2237                 break;
2238         case ContentsTrim:
2239                 show_verbose_cursor_time (pf);
2240                 break;
2241         }
2242
2243         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2244                 i->view->region()->suspend_property_changes ();
2245         }
2246 }
2247
2248 void
2249 TrimDrag::motion (GdkEvent* event, bool first_move)
2250 {
2251         RegionView* rv = _primary;
2252
2253         double speed = 1.0;
2254         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2255         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2256         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2257         frameoffset_t frame_delta = 0;
2258
2259         if (tv && tv->is_track()) {
2260                 speed = tv->track()->speed();
2261         }
2262
2263         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2264
2265         if (first_move) {
2266
2267                 string trim_type;
2268
2269                 switch (_operation) {
2270                 case StartTrim:
2271                         trim_type = "Region start trim";
2272                         break;
2273                 case EndTrim:
2274                         trim_type = "Region end trim";
2275                         break;
2276                 case ContentsTrim:
2277                         trim_type = "Region content trim";
2278                         break;
2279                 default:
2280                         assert(0);
2281                         break;
2282                 }
2283
2284                 _editor->begin_reversible_command (trim_type);
2285
2286                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2287                         RegionView* rv = i->view;
2288                         rv->enable_display (false);
2289                         rv->region()->playlist()->clear_owned_changes ();
2290
2291                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2292
2293                         if (arv) {
2294                                 arv->temporarily_hide_envelope ();
2295                                 arv->drag_start ();
2296                         }
2297
2298                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2299                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2300
2301                         if (insert_result.second) {
2302                                 pl->freeze();
2303                         }
2304                 }
2305         }
2306
2307         bool non_overlap_trim = false;
2308
2309         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2310                 non_overlap_trim = true;
2311         }
2312
2313         /* contstrain trim to fade length */
2314         if (_preserve_fade_anchor) {
2315                 switch (_operation) {
2316                         case StartTrim:
2317                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2318                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2319                                         if (!arv) continue;
2320                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2321                                         if (ar->locked()) continue;
2322                                         framecnt_t len = ar->fade_in()->back()->when;
2323                                         if (len < dt) dt = min(dt, len);
2324                                 }
2325                                 break;
2326                         case EndTrim:
2327                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2328                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2329                                         if (!arv) continue;
2330                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2331                                         if (ar->locked()) continue;
2332                                         framecnt_t len = ar->fade_out()->back()->when;
2333                                         if (len < -dt) dt = max(dt, -len);
2334                                 }
2335                                 break;
2336                         case ContentsTrim:
2337                                 break;
2338                 }
2339         }
2340
2341
2342         switch (_operation) {
2343         case StartTrim:
2344                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2345                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2346                         if (changed && _preserve_fade_anchor) {
2347                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2348                                 if (arv) {
2349                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2350                                         framecnt_t len = ar->fade_in()->back()->when;
2351                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2352                                         framepos_t new_length = len - diff;
2353                                         i->anchored_fade_length = min (ar->length(), new_length);
2354                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2355                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2356                                 }
2357                         }
2358                 }
2359                 break;
2360
2361         case EndTrim:
2362                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2363                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2364                         if (changed && _preserve_fade_anchor) {
2365                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2366                                 if (arv) {
2367                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2368                                         framecnt_t len = ar->fade_out()->back()->when;
2369                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2370                                         framepos_t new_length = len + diff;
2371                                         i->anchored_fade_length = min (ar->length(), new_length);
2372                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2373                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2374                                 }
2375                         }
2376                 }
2377                 break;
2378
2379         case ContentsTrim:
2380                 {
2381                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2382
2383                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2384                                 i->view->move_contents (frame_delta);
2385                         }
2386                 }
2387                 break;
2388         }
2389
2390         switch (_operation) {
2391         case StartTrim:
2392                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2393                 break;
2394         case EndTrim:
2395                 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2396                 break;
2397         case ContentsTrim:
2398                 // show_verbose_cursor_time (frame_delta);
2399                 break;
2400         }
2401 }
2402
2403
2404 void
2405 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2406 {
2407         if (movement_occurred) {
2408                 motion (event, false);
2409
2410                 if (_operation == StartTrim) {
2411                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2412                                 {
2413                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2414                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2415                                            needs to be zero in the undo record.
2416                                         */
2417                                         i->view->trim_front_ending ();
2418                                 }
2419                                 if (_preserve_fade_anchor) {
2420                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2421                                         if (arv) {
2422                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2423                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2424                                                 ar->set_fade_in_length(i->anchored_fade_length);
2425                                                 ar->set_fade_in_active(true);
2426                                         }
2427                                 }
2428                                 if (_jump_position_when_done) {
2429                                         i->view->region()->set_position (i->initial_position);
2430                                 }
2431                         }
2432                 } else if (_operation == EndTrim) {
2433                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2434                                 if (_preserve_fade_anchor) {
2435                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2436                                         if (arv) {
2437                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2438                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2439                                                 ar->set_fade_out_length(i->anchored_fade_length);
2440                                                 ar->set_fade_out_active(true);
2441                                         }
2442                                 }
2443                                 if (_jump_position_when_done) {
2444                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
2445                                 }
2446                         }
2447                 }
2448
2449                 if (!_views.empty()) {
2450                         if (_operation == StartTrim) {
2451                                 _editor->maybe_locate_with_edit_preroll(
2452                                         _views.begin()->view->region()->position());
2453                         }
2454                         if (_operation == EndTrim) {
2455                                 _editor->maybe_locate_with_edit_preroll(
2456                                         _views.begin()->view->region()->position() +
2457                                         _views.begin()->view->region()->length());
2458                         }
2459                 }
2460         
2461                 if (!_editor->selection->selected (_primary)) {
2462                         _primary->thaw_after_trim ();
2463                 } else {
2464
2465                         set<boost::shared_ptr<Playlist> > diffed_playlists;
2466
2467                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2468                                 i->view->thaw_after_trim ();
2469                                 i->view->enable_display (true);
2470
2471                                 /* Trimming one region may affect others on the playlist, so we need
2472                                    to get undo Commands from the whole playlist rather than just the
2473                                    region.  Use diffed_playlists to make sure we don't diff a given
2474                                    playlist more than once.
2475                                 */
2476                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2477                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2478                                         vector<Command*> cmds;
2479                                         p->rdiff (cmds);
2480                                         _editor->session()->add_commands (cmds);
2481                                         diffed_playlists.insert (p);
2482                                 }
2483                         }
2484                 }
2485
2486                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2487                         (*p)->thaw ();
2488                 }
2489
2490                 _editor->motion_frozen_playlists.clear ();
2491                 _editor->commit_reversible_command();
2492
2493         } else {
2494                 /* no mouse movement */
2495                 _editor->point_trim (event, adjusted_current_frame (event));
2496         }
2497
2498         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2499                 if (_operation == StartTrim) {
2500                         i->view->trim_front_ending ();
2501                 }
2502
2503                 i->view->region()->resume_property_changes ();
2504         }
2505 }
2506
2507 void
2508 TrimDrag::aborted (bool movement_occurred)
2509 {
2510         /* Our motion method is changing model state, so use the Undo system
2511            to cancel.  Perhaps not ideal, as this will leave an Undo point
2512            behind which may be slightly odd from the user's point of view.
2513         */
2514
2515         finished (0, true);
2516
2517         if (movement_occurred) {
2518                 _editor->undo ();
2519         }
2520
2521         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2522                 i->view->region()->resume_property_changes ();
2523         }
2524 }
2525
2526 void
2527 TrimDrag::setup_pointer_frame_offset ()
2528 {
2529         list<DraggingView>::iterator i = _views.begin ();
2530         while (i != _views.end() && i->view != _primary) {
2531                 ++i;
2532         }
2533
2534         if (i == _views.end()) {
2535                 return;
2536         }
2537
2538         switch (_operation) {
2539         case StartTrim:
2540                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2541                 break;
2542         case EndTrim:
2543                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2544                 break;
2545         case ContentsTrim:
2546                 break;
2547         }
2548 }
2549
2550 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2551         : Drag (e, i),
2552           _copy (c)
2553 {
2554         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2555         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2556         assert (_marker);
2557 }
2558
2559 void
2560 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2561 {
2562         Drag::start_grab (event, cursor);
2563         show_verbose_cursor_time (adjusted_current_frame(event));
2564 }
2565
2566 void
2567 MeterMarkerDrag::setup_pointer_frame_offset ()
2568 {
2569         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2570 }
2571
2572 void
2573 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2574 {
2575         if (!_marker->meter().movable()) {
2576                 return;
2577         }
2578
2579         if (first_move) {
2580
2581                 // create a dummy marker for visual representation of moving the
2582                 // section, because whether its a copy or not, we're going to 
2583                 // leave or lose the original marker (leave if its a copy; lose if its
2584                 // not, because we'll remove it from the map).
2585                 
2586                 MeterSection section (_marker->meter());
2587
2588                 if (!section.movable()) {
2589                         return;
2590                 }
2591                 
2592                 char name[64];
2593                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2594                 
2595                 _marker = new MeterMarker (
2596                         *_editor,
2597                         *_editor->meter_group,
2598                         ARDOUR_UI::config()->get_MeterMarker(),
2599                         name,
2600                         *new MeterSection (_marker->meter())
2601                 );
2602                 
2603                 /* use the new marker for the grab */
2604                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2605
2606                 if (!_copy) {
2607                         TempoMap& map (_editor->session()->tempo_map());
2608                         /* get current state */
2609                         before_state = &map.get_state();
2610                         /* remove the section while we drag it */
2611                         map.remove_meter (section, true);
2612                 }
2613         }
2614
2615         framepos_t const pf = adjusted_current_frame (event);
2616         _marker->set_position (pf);
2617         show_verbose_cursor_time (pf);
2618 }
2619
2620 void
2621 MeterMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2622 {
2623         if (!movement_occurred) {
2624                 if (was_double_click()) {
2625                         _editor->edit_meter_marker (*_marker);
2626                 }
2627                 return;
2628         }
2629
2630         if (!_marker->meter().movable()) {
2631                 return;
2632         }
2633
2634         motion (event, false);
2635
2636         Timecode::BBT_Time when;
2637
2638         TempoMap& map (_editor->session()->tempo_map());
2639         map.bbt_time (last_pointer_frame(), when);
2640         
2641         if (_copy == true) {
2642                 _editor->begin_reversible_command (_("copy meter mark"));
2643                 XMLNode &before = map.get_state();
2644                 map.add_meter (_marker->meter(), when);
2645                 XMLNode &after = map.get_state();
2646                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, &before, &after));
2647                 _editor->commit_reversible_command ();
2648
2649         } else {
2650                 _editor->begin_reversible_command (_("move meter mark"));
2651
2652                 /* we removed it before, so add it back now */
2653                 
2654                 map.add_meter (_marker->meter(), when);
2655                 XMLNode &after = map.get_state();
2656                 _editor->session()->add_command(new MementoCommand<TempoMap>(map, before_state, &after));
2657                 _editor->commit_reversible_command ();
2658         }
2659
2660         // delete the dummy marker we used for visual representation while moving.
2661         // a new visual marker will show up automatically.
2662         delete _marker;
2663 }
2664
2665 void
2666 MeterMarkerDrag::aborted (bool moved)
2667 {
2668         _marker->set_position (_marker->meter().frame ());
2669
2670         if (moved) {
2671                 TempoMap& map (_editor->session()->tempo_map());
2672                 /* we removed it before, so add it back now */
2673                 map.add_meter (_marker->meter(), _marker->meter().frame());
2674                 // delete the dummy marker we used for visual representation while moving.
2675                 // a new visual marker will show up automatically.
2676                 delete _marker;
2677         }
2678 }
2679
2680 TempoMarkerDrag::TempoMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2681         : Drag (e, i),
2682           _copy (c)
2683 {
2684         DEBUG_TRACE (DEBUG::Drags, "New TempoMarkerDrag\n");
2685
2686         _marker = reinterpret_cast<TempoMarker*> (_item->get_data ("marker"));
2687         assert (_marker);
2688 }
2689
2690 void
2691 TempoMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2692 {
2693         Drag::start_grab (event, cursor);
2694         show_verbose_cursor_time (adjusted_current_frame (event));
2695 }
2696
2697 void
2698 TempoMarkerDrag::setup_pointer_frame_offset ()
2699 {
2700         _pointer_frame_offset = raw_grab_frame() - _marker->tempo().frame();
2701 }
2702
2703 void
2704 TempoMarkerDrag::motion (GdkEvent* event, bool first_move)
2705 {
2706         if (!_marker->tempo().movable()) {
2707                 return;
2708         }
2709
2710         if (first_move) {
2711
2712                 // create a dummy marker for visual representation of moving the
2713                 // section, because whether its a copy or not, we're going to 
2714                 // leave or lose the original marker (leave if its a copy; lose if its
2715                 // not, because we'll remove it from the map).
2716                 
2717                 // create a dummy marker for visual representation of moving the copy.
2718                 // The actual copying is not done before we reach the finish callback.
2719
2720                 char name[64];
2721                 snprintf (name, sizeof (name), "%.2f", _marker->tempo().beats_per_minute());
2722
2723                 TempoSection section (_marker->tempo());
2724
2725                 _marker = new TempoMarker (
2726                         *_editor,
2727                         *_editor->tempo_group,
2728                         ARDOUR_UI::config()->get_TempoMarker(),
2729                         name,
2730                         *new TempoSection (_marker->tempo())
2731                         );
2732
2733                 /* use the new marker for the grab */
2734                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2735
2736                 if (!_copy) {
2737                         TempoMap& map (_editor->session()->tempo_map());
2738                         /* get current state */
2739                         before_state = &map.get_state();
2740                         /* remove the section while we drag it */
2741                         map.remove_tempo (section, true);
2742                 }
2743         }
2744
2745         framepos_t const pf = adjusted_current_frame (event);
2746         _marker->set_position (pf);
2747         show_verbose_cursor_time (pf);
2748 }
2749
2750 void
2751 TempoMarkerDrag::finished (GdkEvent* event, bool movement_occurred)
2752 {
2753         if (!movement_occurred) {
2754                 if (was_double_click()) {
2755                         _editor->edit_tempo_marker (*_marker);
2756                 }
2757                 return;
2758         }
2759
2760         if (!_marker->tempo().movable()) {
2761                 return;
2762         }
2763
2764         motion (event, false);
2765
2766         TempoMap& map (_editor->session()->tempo_map());
2767         framepos_t beat_time = map.round_to_beat (last_pointer_frame(), RoundNearest);
2768         Timecode::BBT_Time when;
2769
2770         map.bbt_time (beat_time, when);
2771
2772         if (_copy == true) {
2773                 _editor->begin_reversible_command (_("copy tempo mark"));
2774                 XMLNode &before = map.get_state();
2775                 map.add_tempo (_marker->tempo(), when);
2776                 XMLNode &after = map.get_state();
2777                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, &before, &after));
2778                 _editor->commit_reversible_command ();
2779
2780         } else {
2781                 _editor->begin_reversible_command (_("move tempo mark"));
2782                 /* we removed it before, so add it back now */
2783                 map.add_tempo (_marker->tempo(), when);
2784                 XMLNode &after = map.get_state();
2785                 _editor->session()->add_command (new MementoCommand<TempoMap>(map, before_state, &after));
2786                 _editor->commit_reversible_command ();
2787         }
2788
2789         // delete the dummy marker we used for visual representation while moving.
2790         // a new visual marker will show up automatically.
2791         delete _marker;
2792 }
2793
2794 void
2795 TempoMarkerDrag::aborted (bool moved)
2796 {
2797         _marker->set_position (_marker->tempo().frame());
2798         if (moved) {
2799                 TempoMap& map (_editor->session()->tempo_map());
2800                 /* we removed it before, so add it back now */
2801                 map.add_tempo (_marker->tempo(), _marker->tempo().start());
2802                 // delete the dummy marker we used for visual representation while moving.
2803                 // a new visual marker will show up automatically.
2804                 delete _marker;
2805         }
2806 }
2807
2808 CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
2809         : Drag (e, &c.track_canvas_item(), false)
2810         , _cursor (c)
2811         , _stop (s)
2812 {
2813         DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
2814 }
2815
2816 /** Do all the things we do when dragging the playhead to make it look as though
2817  *  we have located, without actually doing the locate (because that would cause
2818  *  the diskstream buffers to be refilled, which is too slow).
2819  */
2820 void
2821 CursorDrag::fake_locate (framepos_t t)
2822 {
2823         _editor->playhead_cursor->set_position (t);
2824
2825         Session* s = _editor->session ();
2826         if (s->timecode_transmission_suspended ()) {
2827                 framepos_t const f = _editor->playhead_cursor->current_frame ();
2828                 /* This is asynchronous so it will be sent "now"
2829                  */
2830                 s->send_mmc_locate (f);
2831                 /* These are synchronous and will be sent during the next
2832                    process cycle
2833                 */
2834                 s->queue_full_time_code ();
2835                 s->queue_song_position_pointer ();
2836         }
2837
2838         show_verbose_cursor_time (t);
2839         _editor->UpdateAllTransportClocks (t);
2840 }
2841
2842 void
2843 CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
2844 {
2845         Drag::start_grab (event, c);
2846
2847         _grab_zoom = _editor->samples_per_pixel;
2848
2849         framepos_t where = _editor->canvas_event_sample (event);
2850
2851         _editor->snap_to_with_modifier (where, event);
2852
2853         _editor->_dragging_playhead = true;
2854
2855         Session* s = _editor->session ();
2856
2857         /* grab the track canvas item as well */
2858
2859         _cursor.track_canvas_item().grab();
2860
2861         if (s) {
2862                 if (_was_rolling && _stop) {
2863                         s->request_stop ();
2864                 }
2865
2866                 if (s->is_auditioning()) {
2867                         s->cancel_audition ();
2868                 }
2869
2870
2871                 if (AudioEngine::instance()->connected()) {
2872                         
2873                         /* do this only if we're the engine is connected
2874                          * because otherwise this request will never be
2875                          * serviced and we'll busy wait forever. likewise,
2876                          * notice if we are disconnected while waiting for the
2877                          * request to be serviced.
2878                          */
2879
2880                         s->request_suspend_timecode_transmission ();
2881                         while (AudioEngine::instance()->connected() && !s->timecode_transmission_suspended ()) {
2882                                 /* twiddle our thumbs */
2883                         }
2884                 }
2885         }
2886
2887         fake_locate (where);
2888 }
2889
2890 void
2891 CursorDrag::motion (GdkEvent* event, bool)
2892 {
2893         framepos_t const adjusted_frame = adjusted_current_frame (event);
2894         if (adjusted_frame != last_pointer_frame()) {
2895                 fake_locate (adjusted_frame);
2896         }
2897 }
2898
2899 void
2900 CursorDrag::finished (GdkEvent* event, bool movement_occurred)
2901 {
2902         _editor->_dragging_playhead = false;
2903
2904         _cursor.track_canvas_item().ungrab();
2905
2906         if (!movement_occurred && _stop) {
2907                 return;
2908         }
2909
2910         motion (event, false);
2911
2912         Session* s = _editor->session ();
2913         if (s) {
2914                 s->request_locate (_editor->playhead_cursor->current_frame (), _was_rolling);
2915                 _editor->_pending_locate_request = true;
2916                 s->request_resume_timecode_transmission ();
2917         }
2918 }
2919
2920 void
2921 CursorDrag::aborted (bool)
2922 {
2923         _cursor.track_canvas_item().ungrab();
2924
2925         if (_editor->_dragging_playhead) {
2926                 _editor->session()->request_resume_timecode_transmission ();
2927                 _editor->_dragging_playhead = false;
2928         }
2929
2930         _editor->playhead_cursor->set_position (adjusted_frame (grab_frame (), 0, false));
2931 }
2932
2933 FadeInDrag::FadeInDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
2934         : RegionDrag (e, i, p, v)
2935 {
2936         DEBUG_TRACE (DEBUG::Drags, "New FadeInDrag\n");
2937 }
2938
2939 void
2940 FadeInDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2941 {
2942         Drag::start_grab (event, cursor);
2943
2944         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2945         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2946
2947         show_verbose_cursor_duration (r->position(), r->position() + r->fade_in()->back()->when, 32);
2948 }
2949
2950 void
2951 FadeInDrag::setup_pointer_frame_offset ()
2952 {
2953         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
2954         boost::shared_ptr<AudioRegion> const r = arv->audio_region ();
2955         _pointer_frame_offset = raw_grab_frame() - ((framecnt_t) r->fade_in()->back()->when + r->position());
2956 }
2957
2958 void
2959 FadeInDrag::motion (GdkEvent* event, bool)
2960 {
2961         framecnt_t fade_length;
2962
2963         framepos_t const pos = adjusted_current_frame (event);
2964
2965         boost::shared_ptr<Region> region = _primary->region ();
2966
2967         if (pos < (region->position() + 64)) {
2968                 fade_length = 64; // this should be a minimum defined somewhere
2969         } else if (pos > region->last_frame()) {
2970                 fade_length = region->length();
2971         } else {
2972                 fade_length = pos - region->position();
2973         }
2974
2975         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2976
2977                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
2978
2979                 if (!tmp) {
2980                         continue;
2981                 }
2982
2983                 tmp->reset_fade_in_shape_width (tmp->audio_region(), fade_length);
2984         }
2985
2986         show_verbose_cursor_duration (region->position(), region->position() + fade_length, 32);
2987 }
2988
2989 void
2990 FadeInDrag::finished (GdkEvent* event, bool movement_occurred)
2991 {
2992         if (!movement_occurred) {
2993                 return;
2994         }
2995
2996         framecnt_t fade_length;
2997
2998         framepos_t const pos = adjusted_current_frame (event);
2999
3000         boost::shared_ptr<Region> region = _primary->region ();
3001
3002         if (pos < (region->position() + 64)) {
3003                 fade_length = 64; // this should be a minimum defined somewhere
3004         } else if (pos > region->last_frame()) {
3005                 fade_length = region->length();
3006         } else {
3007                 fade_length = pos - region->position();
3008         }
3009
3010         _editor->begin_reversible_command (_("change fade in length"));
3011
3012         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3013
3014                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3015
3016                 if (!tmp) {
3017                         continue;
3018                 }
3019
3020                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
3021                 XMLNode &before = alist->get_state();
3022
3023                 tmp->audio_region()->set_fade_in_length (fade_length);
3024                 tmp->audio_region()->set_fade_in_active (true);
3025
3026                 XMLNode &after = alist->get_state();
3027                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3028         }
3029
3030         _editor->commit_reversible_command ();
3031 }
3032
3033 void
3034 FadeInDrag::aborted (bool)
3035 {
3036         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3037                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3038
3039                 if (!tmp) {
3040                         continue;
3041                 }
3042
3043                 tmp->reset_fade_in_shape_width (tmp->audio_region(), tmp->audio_region()->fade_in()->back()->when);
3044         }
3045 }
3046
3047 FadeOutDrag::FadeOutDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
3048         : RegionDrag (e, i, p, v)
3049 {
3050         DEBUG_TRACE (DEBUG::Drags, "New FadeOutDrag\n");
3051 }
3052
3053 void
3054 FadeOutDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3055 {
3056         Drag::start_grab (event, cursor);
3057
3058         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3059         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3060
3061         show_verbose_cursor_duration (r->last_frame() - r->fade_out()->back()->when, r->last_frame());
3062 }
3063
3064 void
3065 FadeOutDrag::setup_pointer_frame_offset ()
3066 {
3067         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (_primary);
3068         boost::shared_ptr<AudioRegion> r = arv->audio_region ();
3069         _pointer_frame_offset = raw_grab_frame() - (r->length() - (framecnt_t) r->fade_out()->back()->when + r->position());
3070 }
3071
3072 void
3073 FadeOutDrag::motion (GdkEvent* event, bool)
3074 {
3075         framecnt_t fade_length;
3076
3077         framepos_t const pos = adjusted_current_frame (event);
3078
3079         boost::shared_ptr<Region> region = _primary->region ();
3080
3081         if (pos > (region->last_frame() - 64)) {
3082                 fade_length = 64; // this should really be a minimum fade defined somewhere
3083         }
3084         else if (pos < region->position()) {
3085                 fade_length = region->length();
3086         }
3087         else {
3088                 fade_length = region->last_frame() - pos;
3089         }
3090
3091         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3092
3093                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3094
3095                 if (!tmp) {
3096                         continue;
3097                 }
3098
3099                 tmp->reset_fade_out_shape_width (tmp->audio_region(), fade_length);
3100         }
3101
3102         show_verbose_cursor_duration (region->last_frame() - fade_length, region->last_frame());
3103 }
3104
3105 void
3106 FadeOutDrag::finished (GdkEvent* event, bool movement_occurred)
3107 {
3108         if (!movement_occurred) {
3109                 return;
3110         }
3111
3112         framecnt_t fade_length;
3113
3114         framepos_t const pos = adjusted_current_frame (event);
3115
3116         boost::shared_ptr<Region> region = _primary->region ();
3117
3118         if (pos > (region->last_frame() - 64)) {
3119                 fade_length = 64; // this should really be a minimum fade defined somewhere
3120         }
3121         else if (pos < region->position()) {
3122                 fade_length = region->length();
3123         }
3124         else {
3125                 fade_length = region->last_frame() - pos;
3126         }
3127
3128         _editor->begin_reversible_command (_("change fade out length"));
3129
3130         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3131
3132                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3133
3134                 if (!tmp) {
3135                         continue;
3136                 }
3137
3138                 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
3139                 XMLNode &before = alist->get_state();
3140
3141                 tmp->audio_region()->set_fade_out_length (fade_length);
3142                 tmp->audio_region()->set_fade_out_active (true);
3143
3144                 XMLNode &after = alist->get_state();
3145                 _editor->session()->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
3146         }
3147
3148         _editor->commit_reversible_command ();
3149 }
3150
3151 void
3152 FadeOutDrag::aborted (bool)
3153 {
3154         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
3155                 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (i->view);
3156
3157                 if (!tmp) {
3158                         continue;
3159                 }
3160
3161                 tmp->reset_fade_out_shape_width (tmp->audio_region(), tmp->audio_region()->fade_out()->back()->when);
3162         }
3163 }
3164
3165 MarkerDrag::MarkerDrag (Editor* e, ArdourCanvas::Item* i)
3166         : Drag (e, i)
3167 {
3168         DEBUG_TRACE (DEBUG::Drags, "New MarkerDrag\n");
3169
3170         _marker = reinterpret_cast<Marker*> (_item->get_data ("marker"));
3171         assert (_marker);
3172
3173         _points.push_back (ArdourCanvas::Duple (0, 0));
3174         _points.push_back (ArdourCanvas::Duple (0, physical_screen_height (_editor->get_window())));
3175 }
3176
3177 MarkerDrag::~MarkerDrag ()
3178 {
3179         for (CopiedLocationInfo::iterator i = _copied_locations.begin(); i != _copied_locations.end(); ++i) {
3180                 delete i->location;
3181         }
3182 }
3183
3184 MarkerDrag::CopiedLocationMarkerInfo::CopiedLocationMarkerInfo (Location* l, Marker* m)
3185 {
3186         location = new Location (*l);
3187         markers.push_back (m);
3188         move_both = false;
3189 }
3190
3191 void
3192 MarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3193 {
3194         Drag::start_grab (event, cursor);
3195
3196         bool is_start;
3197
3198         Location *location = _editor->find_location_from_marker (_marker, is_start);
3199         _editor->_dragging_edit_point = true;
3200
3201         update_item (location);
3202
3203         // _drag_line->show();
3204         // _line->raise_to_top();
3205
3206         if (is_start) {
3207                 show_verbose_cursor_time (location->start());
3208         } else {
3209                 show_verbose_cursor_time (location->end());
3210         }
3211
3212         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3213
3214         switch (op) {
3215         case Selection::Toggle:
3216                 /* we toggle on the button release */
3217                 break;
3218         case Selection::Set:
3219                 if (!_editor->selection->selected (_marker)) {
3220                         _editor->selection->set (_marker);
3221                 }
3222                 break;
3223         case Selection::Extend:
3224         {
3225                 Locations::LocationList ll;
3226                 list<Marker*> to_add;
3227                 framepos_t s, e;
3228                 _editor->selection->markers.range (s, e);
3229                 s = min (_marker->position(), s);
3230                 e = max (_marker->position(), e);
3231                 s = min (s, e);
3232                 e = max (s, e);
3233                 if (e < max_framepos) {
3234                         ++e;
3235                 }
3236                 _editor->session()->locations()->find_all_between (s, e, ll, Location::Flags (0));
3237                 for (Locations::LocationList::iterator i = ll.begin(); i != ll.end(); ++i) {
3238                         Editor::LocationMarkers* lm = _editor->find_location_markers (*i);
3239                         if (lm) {
3240                                 if (lm->start) {
3241                                         to_add.push_back (lm->start);
3242                                 }
3243                                 if (lm->end) {
3244                                         to_add.push_back (lm->end);
3245                                 }
3246                         }
3247                 }
3248                 if (!to_add.empty()) {
3249                         _editor->selection->add (to_add);
3250                 }
3251                 break;
3252         }
3253         case Selection::Add:
3254                 _editor->selection->add (_marker);
3255                 break;
3256         }
3257
3258         /* Set up copies for us to manipulate during the drag 
3259          */
3260
3261         for (MarkerSelection::iterator i = _editor->selection->markers.begin(); i != _editor->selection->markers.end(); ++i) {
3262
3263                 Location* l = _editor->find_location_from_marker (*i, is_start);
3264
3265                 if (!l) {
3266                         continue;
3267                 }
3268
3269                 if (l->is_mark()) {
3270                         _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3271                 } else {
3272                         /* range: check that the other end of the range isn't
3273                            already there.
3274                         */
3275                         CopiedLocationInfo::iterator x;
3276                         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3277                                 if (*(*x).location == *l) {
3278                                         break;
3279                                 }
3280                         }
3281                         if (x == _copied_locations.end()) {
3282                                 _copied_locations.push_back (CopiedLocationMarkerInfo (l, *i));
3283                         } else {
3284                                 (*x).markers.push_back (*i);
3285                                 (*x).move_both = true;
3286                         }
3287                 }
3288                         
3289         }
3290 }
3291
3292 void
3293 MarkerDrag::setup_pointer_frame_offset ()
3294 {
3295         bool is_start;
3296         Location *location = _editor->find_location_from_marker (_marker, is_start);
3297         _pointer_frame_offset = raw_grab_frame() - (is_start ? location->start() : location->end());
3298 }
3299
3300 void
3301 MarkerDrag::motion (GdkEvent* event, bool)
3302 {
3303         framecnt_t f_delta = 0;
3304         bool is_start;
3305         bool move_both = false;
3306         Location *real_location;
3307         Location *copy_location = 0;
3308
3309         framepos_t const newframe = adjusted_current_frame (event);
3310         framepos_t next = newframe;
3311
3312         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
3313                 move_both = true;
3314         }
3315
3316         CopiedLocationInfo::iterator x;
3317
3318         /* find the marker we're dragging, and compute the delta */
3319
3320         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3321
3322                 copy_location = (*x).location;
3323
3324                 if (find (x->markers.begin(), x->markers.end(), _marker) != x->markers.end()) {
3325
3326                         /* this marker is represented by this
3327                          * CopiedLocationMarkerInfo 
3328                          */
3329
3330                         if ((real_location = _editor->find_location_from_marker (_marker, is_start)) == 0) {
3331                                 /* que pasa ?? */
3332                                 return;
3333                         }
3334
3335                         if (real_location->is_mark()) {
3336                                 f_delta = newframe - copy_location->start();
3337                         } else {
3338
3339
3340                                 switch (_marker->type()) {
3341                                 case Marker::SessionStart:
3342                                 case Marker::RangeStart:
3343                                 case Marker::LoopStart:
3344                                 case Marker::PunchIn:
3345                                         f_delta = newframe - copy_location->start();
3346                                         break;
3347
3348                                 case Marker::SessionEnd:
3349                                 case Marker::RangeEnd:
3350                                 case Marker::LoopEnd:
3351                                 case Marker::PunchOut:
3352                                         f_delta = newframe - copy_location->end();
3353                                         break;
3354                                 default:
3355                                         /* what kind of marker is this ? */
3356                                         return;
3357                                 }
3358                         }
3359
3360                         break;
3361                 }
3362         }
3363
3364         if (x == _copied_locations.end()) {
3365                 /* hmm, impossible - we didn't find the dragged marker */
3366                 return;
3367         }
3368
3369         /* now move them all */
3370
3371         for (x = _copied_locations.begin(); x != _copied_locations.end(); ++x) {
3372
3373                 copy_location = x->location;
3374
3375                 /* call this to find out if its the start or end */
3376
3377                 if ((real_location = _editor->find_location_from_marker (x->markers.front(), is_start)) == 0) {
3378                         continue;
3379                 }
3380
3381                 if (real_location->locked()) {
3382                         continue;
3383                 }
3384
3385                 if (copy_location->is_mark()) {
3386
3387                         /* now move it */
3388
3389                         copy_location->set_start (copy_location->start() + f_delta);
3390
3391                 } else {
3392                         
3393                         framepos_t new_start = copy_location->start() + f_delta;
3394                         framepos_t new_end = copy_location->end() + f_delta;
3395                         
3396                         if (is_start) { // start-of-range marker
3397                                 
3398                                 if (move_both || (*x).move_both) {
3399                                         copy_location->set_start (new_start);
3400                                         copy_location->set_end (new_end);
3401                                 } else  if (new_start < copy_location->end()) {
3402                                         copy_location->set_start (new_start);
3403                                 } else if (newframe > 0) {
3404                                         _editor->snap_to (next, RoundUpAlways, true);
3405                                         copy_location->set_end (next);
3406                                         copy_location->set_start (newframe);
3407                                 }
3408
3409                         } else { // end marker
3410
3411                                 if (move_both || (*x).move_both) {
3412                                         copy_location->set_end (new_end);
3413                                         copy_location->set_start (new_start);
3414                                 } else if (new_end > copy_location->start()) {
3415                                         copy_location->set_end (new_end);
3416                                 } else if (newframe > 0) {
3417                                         _editor->snap_to (next, RoundDownAlways, true);
3418                                         copy_location->set_start (next);
3419                                         copy_location->set_end (newframe);
3420                                 }
3421                         }
3422                 }
3423
3424                 update_item (copy_location);
3425                 
3426                 /* now lookup the actual GUI items used to display this
3427                  * location and move them to wherever the copy of the location
3428                  * is now. This means that the logic in ARDOUR::Location is
3429                  * still enforced, even though we are not (yet) modifying 
3430                  * the real Location itself.
3431                  */
3432                 
3433                 Editor::LocationMarkers* lm = _editor->find_location_markers (real_location);
3434
3435                 if (lm) {
3436                         lm->set_position (copy_location->start(), copy_location->end());
3437                 }
3438
3439         }
3440
3441         assert (!_copied_locations.empty());
3442
3443         show_verbose_cursor_time (newframe);
3444 }
3445
3446 void
3447 MarkerDrag::finished (GdkEvent* event, bool movement_occurred)
3448 {
3449         if (!movement_occurred) {
3450                 
3451                 if (was_double_click()) {
3452                         _editor->rename_marker (_marker);
3453                         return;
3454                 }
3455
3456                 /* just a click, do nothing but finish
3457                    off the selection process
3458                 */
3459
3460                 Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
3461
3462                 switch (op) {
3463                 case Selection::Set:
3464                         if (_editor->selection->selected (_marker) && _editor->selection->markers.size() > 1) {
3465                                 _editor->selection->set (_marker);
3466                         }
3467                         break;
3468
3469                 case Selection::Toggle:
3470                         /* we toggle on the button release, click only */
3471                         _editor->selection->toggle (_marker);
3472                         break;
3473
3474                 case Selection::Extend:
3475                 case Selection::Add:
3476                         break;
3477                 }
3478
3479                 return;
3480         }
3481
3482         _editor->_dragging_edit_point = false;
3483
3484         _editor->begin_reversible_command ( _("move marker") );
3485         XMLNode &before = _editor->session()->locations()->get_state();
3486
3487         MarkerSelection::iterator i;
3488         CopiedLocationInfo::iterator x;
3489         bool is_start;
3490
3491         for (i = _editor->selection->markers.begin(), x = _copied_locations.begin();
3492              x != _copied_locations.end() && i != _editor->selection->markers.end();
3493              ++i, ++x) {
3494
3495                 Location * location = _editor->find_location_from_marker (*i, is_start);
3496
3497                 if (location) {
3498
3499                         if (location->locked()) {
3500                                 return;
3501                         }
3502
3503                         if (location->is_mark()) {
3504                                 location->set_start (((*x).location)->start());
3505                         } else {
3506                                 location->set (((*x).location)->start(), ((*x).location)->end());
3507                         }
3508                 }
3509         }
3510
3511         XMLNode &after = _editor->session()->locations()->get_state();
3512         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
3513         _editor->commit_reversible_command ();
3514 }
3515
3516 void
3517 MarkerDrag::aborted (bool)
3518 {
3519         /* XXX: TODO */
3520 }
3521
3522 void
3523 MarkerDrag::update_item (Location*)
3524 {
3525         /* noop */
3526 }
3527
3528 ControlPointDrag::ControlPointDrag (Editor* e, ArdourCanvas::Item* i)
3529         : Drag (e, i),
3530           _cumulative_x_drag (0),
3531           _cumulative_y_drag (0)
3532 {
3533         if (_zero_gain_fraction < 0.0) {
3534                 _zero_gain_fraction = gain_to_slider_position_with_max (dB_to_coefficient (0.0), Config->get_max_gain());
3535         }
3536
3537         DEBUG_TRACE (DEBUG::Drags, "New ControlPointDrag\n");
3538
3539         _point = reinterpret_cast<ControlPoint*> (_item->get_data ("control_point"));
3540         assert (_point);
3541 }
3542
3543
3544 void
3545 ControlPointDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3546 {
3547         Drag::start_grab (event, _editor->cursors()->fader);
3548
3549         // start the grab at the center of the control point so
3550         // the point doesn't 'jump' to the mouse after the first drag
3551         _fixed_grab_x = _point->get_x();
3552         _fixed_grab_y = _point->get_y();
3553
3554         float const fraction = 1 - (_point->get_y() / _point->line().height());
3555
3556         _point->line().start_drag_single (_point, _fixed_grab_x, fraction);
3557
3558         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3559
3560         _pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
3561
3562         if (!_point->can_slide ()) {
3563                 _x_constrained = true;
3564         }
3565 }
3566
3567 void
3568 ControlPointDrag::motion (GdkEvent* event, bool)
3569 {
3570         double dx = _drags->current_pointer_x() - last_pointer_x();
3571         double dy = current_pointer_y() - last_pointer_y();
3572
3573         if (event->button.state & Keyboard::SecondaryModifier) {
3574                 dx *= 0.1;
3575                 dy *= 0.1;
3576         }
3577
3578         /* coordinate in pixels relative to the start of the region (for region-based automation)
3579            or track (for track-based automation) */
3580         double cx = _fixed_grab_x + _cumulative_x_drag + dx;
3581         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3582
3583         // calculate zero crossing point. back off by .01 to stay on the
3584         // positive side of zero
3585         double const zero_gain_y = (1.0 - _zero_gain_fraction) * _point->line().height() - .01;
3586
3587         // make sure we hit zero when passing through
3588         if ((cy < zero_gain_y && (cy - dy) > zero_gain_y) || (cy > zero_gain_y && (cy - dy) < zero_gain_y)) {
3589                 cy = zero_gain_y;
3590         }
3591
3592         if (_x_constrained) {
3593                 cx = _fixed_grab_x;
3594         }
3595         if (_y_constrained) {
3596                 cy = _fixed_grab_y;
3597         }
3598
3599         _cumulative_x_drag = cx - _fixed_grab_x;
3600         _cumulative_y_drag = cy - _fixed_grab_y;
3601
3602         cx = max (0.0, cx);
3603         cy = max (0.0, cy);
3604         cy = min ((double) _point->line().height(), cy);
3605
3606         framepos_t cx_frames = _editor->pixel_to_sample (cx);
3607
3608         if (!_x_constrained) {
3609                 _editor->snap_to_with_modifier (cx_frames, event);
3610         }
3611
3612         cx_frames = min (cx_frames, _point->line().maximum_time());
3613
3614         float const fraction = 1.0 - (cy / _point->line().height());
3615
3616         _point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
3617
3618         show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
3619 }
3620
3621 void
3622 ControlPointDrag::finished (GdkEvent* event, bool movement_occurred)
3623 {
3624         if (!movement_occurred) {
3625
3626                 /* just a click */
3627
3628                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
3629                         _editor->reset_point_selection ();
3630                 }
3631
3632         } else {
3633                 motion (event, false);
3634         }
3635
3636         _point->line().end_drag (_pushing, _final_index);
3637         _editor->session()->commit_reversible_command ();
3638 }
3639
3640 void
3641 ControlPointDrag::aborted (bool)
3642 {
3643         _point->line().reset ();
3644 }
3645
3646 bool
3647 ControlPointDrag::active (Editing::MouseMode m)
3648 {
3649         if (m == Editing::MouseGain) {
3650                 /* always active in mouse gain */
3651                 return true;
3652         }
3653
3654         /* otherwise active if the point is on an automation line (ie not if its on a region gain line) */
3655         return dynamic_cast<AutomationLine*> (&(_point->line())) != 0;
3656 }
3657
3658 LineDrag::LineDrag (Editor* e, ArdourCanvas::Item* i)
3659         : Drag (e, i),
3660           _line (0),
3661           _cumulative_y_drag (0)
3662 {
3663         DEBUG_TRACE (DEBUG::Drags, "New LineDrag\n");
3664 }
3665
3666 void
3667 LineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3668 {
3669         _line = reinterpret_cast<AutomationLine*> (_item->get_data ("line"));
3670         assert (_line);
3671
3672         _item = &_line->grab_item ();
3673
3674         /* need to get x coordinate in terms of parent (TimeAxisItemView)
3675            origin, and ditto for y.
3676         */
3677
3678         double cx = event->button.x;
3679         double cy = event->button.y;
3680
3681         _line->parent_group().canvas_to_item (cx, cy);
3682
3683         framecnt_t const frame_within_region = (framecnt_t) floor (cx * _editor->samples_per_pixel);
3684
3685         uint32_t before;
3686         uint32_t after;
3687
3688         if (!_line->control_points_adjacent (frame_within_region, before, after)) {
3689                 /* no adjacent points */
3690                 return;
3691         }
3692
3693         Drag::start_grab (event, _editor->cursors()->fader);
3694
3695         /* store grab start in parent frame */
3696
3697         _fixed_grab_x = cx;
3698         _fixed_grab_y = cy;
3699
3700         double fraction = 1.0 - (cy / _line->height());
3701
3702         _line->start_drag_line (before, after, fraction);
3703
3704         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3705 }
3706
3707 void
3708 LineDrag::motion (GdkEvent* event, bool)
3709 {
3710         double dy = current_pointer_y() - last_pointer_y();
3711
3712         if (event->button.state & Keyboard::SecondaryModifier) {
3713                 dy *= 0.1;
3714         }
3715
3716         double cy = _fixed_grab_y + _cumulative_y_drag + dy;
3717
3718         _cumulative_y_drag = cy - _fixed_grab_y;
3719
3720         cy = max (0.0, cy);
3721         cy = min ((double) _line->height(), cy);
3722
3723         double const fraction = 1.0 - (cy / _line->height());
3724         uint32_t ignored;
3725
3726         /* we are ignoring x position for this drag, so we can just pass in anything */
3727         _line->drag_motion (0, fraction, true, false, ignored);
3728
3729         show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
3730 }
3731
3732 void
3733 LineDrag::finished (GdkEvent* event, bool movement_occured)
3734 {
3735         if (movement_occured) {
3736                 motion (event, false);
3737                 _line->end_drag (false, 0);
3738         } else {
3739                 /* add a new control point on the line */
3740
3741                 AutomationTimeAxisView* atv;
3742
3743                 _line->end_drag (false, 0);
3744
3745                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3746                         framepos_t where = _editor->window_event_sample (event, 0, 0);
3747                         atv->add_automation_event (event, where, event->button.y, false);
3748                 }
3749         }
3750
3751         _editor->session()->commit_reversible_command ();
3752 }
3753
3754 void
3755 LineDrag::aborted (bool)
3756 {
3757         _line->reset ();
3758 }
3759
3760 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3761         : Drag (e, i),
3762           _line (0),
3763           _cumulative_x_drag (0)
3764 {
3765         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3766 }
3767
3768 void
3769 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3770 {
3771         Drag::start_grab (event);
3772
3773         _line = reinterpret_cast<Line*> (_item);
3774         assert (_line);
3775
3776         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3777
3778         double cx = event->button.x;
3779         double cy = event->button.y;
3780
3781         _item->parent()->canvas_to_item (cx, cy);
3782
3783         /* store grab start in parent frame */
3784         _region_view_grab_x = cx;
3785
3786         _before = *(float*) _item->get_data ("position");
3787
3788         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3789
3790         _max_x = _editor->sample_to_pixel(_arv->get_duration());
3791 }
3792
3793 void
3794 FeatureLineDrag::motion (GdkEvent*, bool)
3795 {
3796         double dx = _drags->current_pointer_x() - last_pointer_x();
3797
3798         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3799
3800         _cumulative_x_drag += dx;
3801
3802         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3803
3804         if (cx > _max_x){
3805                 cx = _max_x;
3806         }
3807         else if(cx < 0){
3808                 cx = 0;
3809         }
3810
3811         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3812         assert (bbox);
3813         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3814
3815         float *pos = new float;
3816         *pos = cx;
3817
3818         _line->set_data ("position", pos);
3819
3820         _before = cx;
3821 }
3822
3823 void
3824 FeatureLineDrag::finished (GdkEvent*, bool)
3825 {
3826         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3827         _arv->update_transient(_before, _before);
3828 }
3829
3830 void
3831 FeatureLineDrag::aborted (bool)
3832 {
3833         //_line->reset ();
3834 }
3835
3836 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3837         : Drag (e, i)
3838         , _vertical_only (false)
3839 {
3840         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3841 }
3842
3843 void
3844 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3845 {
3846         Drag::start_grab (event);
3847         show_verbose_cursor_time (adjusted_current_frame (event));
3848 }
3849
3850 void
3851 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3852 {
3853         framepos_t start;
3854         framepos_t end;
3855         double y1;
3856         double y2;
3857
3858         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3859
3860         framepos_t grab = grab_frame ();
3861         if (Config->get_rubberbanding_snaps_to_grid ()) {
3862                 _editor->snap_to_with_modifier (grab, event);
3863         }
3864
3865         /* base start and end on initial click position */
3866
3867         if (pf < grab) {
3868                 start = pf;
3869                 end = grab;
3870         } else {
3871                 end = pf;
3872                 start = grab;
3873         }
3874
3875         if (current_pointer_y() < grab_y()) {
3876                 y1 = current_pointer_y();
3877                 y2 = grab_y();
3878         } else {
3879                 y2 = current_pointer_y();
3880                 y1 = grab_y();
3881         }
3882
3883         if (start != end || y1 != y2) {
3884
3885                 double x1 = _editor->sample_to_pixel (start);
3886                 double x2 = _editor->sample_to_pixel (end);
3887                 const double min_dimension = 2.0;
3888
3889                 if (_vertical_only) {
3890                         /* fixed 10 pixel width */
3891                         x2 = x1 + 10;
3892                 } else {
3893                         if (x2 < x1) {
3894                                 x2 = min (x1 - min_dimension, x2);
3895                         } else {
3896                                 x2 = max (x1 + min_dimension, x2);
3897                         }
3898                 } 
3899
3900                 if (y2 < y1) {
3901                         y2 = min (y1 - min_dimension, y2);
3902                 } else {
3903                         y2 = max (y1 + min_dimension, y2);
3904                 }
3905
3906                 /* translate rect into item space and set */
3907
3908                 ArdourCanvas::Rect r (x1, y1, x2, y2);
3909
3910                 /* this drag is a _trackview_only == true drag, so the y1 and
3911                  * y2 (computed using current_pointer_y() and grab_y()) will be
3912                  * relative to the top of the trackview group). The
3913                  * rubberband rect has the same parent/scroll offset as the
3914                  * the trackview group, so we can use the "r" rect directly
3915                  * to set the shape of the rubberband.
3916                  */
3917
3918                 _editor->rubberband_rect->set (r);
3919                 _editor->rubberband_rect->show();
3920                 _editor->rubberband_rect->raise_to_top();
3921
3922                 show_verbose_cursor_time (pf);
3923
3924                 do_select_things (event, true);
3925         }
3926 }
3927
3928 void
3929 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3930 {
3931         framepos_t x1;
3932         framepos_t x2;
3933         
3934         if (grab_frame() < last_pointer_frame()) {
3935                 x1 = grab_frame ();
3936                 x2 = last_pointer_frame ();
3937         } else {
3938                 x2 = grab_frame ();
3939                 x1 = last_pointer_frame ();
3940         }
3941
3942         double y1;
3943         double y2;
3944         
3945         if (current_pointer_y() < grab_y()) {
3946                 y1 = current_pointer_y();
3947                 y2 = grab_y();
3948         } else {
3949                 y2 = current_pointer_y();
3950                 y1 = grab_y();
3951         }
3952
3953         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3954 }
3955
3956 void
3957 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3958 {
3959         if (movement_occurred) {
3960
3961                 motion (event, false);
3962                 do_select_things (event, false);
3963
3964         } else {
3965
3966                 /* just a click */
3967
3968                 bool do_deselect = true;
3969                 MidiTimeAxisView* mtv;
3970
3971                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3972                         /* MIDI track */
3973                         if (_editor->selection->empty() && _editor->mouse_mode == MouseDraw) {
3974                                 /* nothing selected */
3975                                 add_midi_region (mtv);
3976                                 do_deselect = false;
3977                         }
3978                 } 
3979
3980                 /* do not deselect if Primary or Tertiary (toggle-select or
3981                  * extend-select are pressed.
3982                  */
3983
3984                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) && 
3985                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) && 
3986                     do_deselect) {
3987                         deselect_things ();
3988                 }
3989
3990         }
3991
3992         _editor->rubberband_rect->hide();
3993 }
3994
3995 void
3996 RubberbandSelectDrag::aborted (bool)
3997 {
3998         _editor->rubberband_rect->hide ();
3999 }
4000
4001 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
4002         : RegionDrag (e, i, p, v)
4003 {
4004         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
4005 }
4006
4007 void
4008 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4009 {
4010         Drag::start_grab (event, cursor);
4011
4012         show_verbose_cursor_time (adjusted_current_frame (event));
4013 }
4014
4015 void
4016 TimeFXDrag::motion (GdkEvent* event, bool)
4017 {
4018         RegionView* rv = _primary;
4019         StreamView* cv = rv->get_time_axis_view().view ();
4020
4021         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
4022         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
4023         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
4024
4025         framepos_t const pf = adjusted_current_frame (event);
4026
4027         if (pf > rv->region()->position()) {
4028                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
4029         }
4030
4031         show_verbose_cursor_time (pf);
4032 }
4033
4034 void
4035 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4036 {
4037         _primary->get_time_axis_view().hide_timestretch ();
4038
4039         if (!movement_occurred) {
4040                 return;
4041         }
4042
4043         if (last_pointer_frame() < _primary->region()->position()) {
4044                 /* backwards drag of the left edge - not usable */
4045                 return;
4046         }
4047
4048         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
4049
4050         float percentage = (double) newlen / (double) _primary->region()->length();
4051
4052 #ifndef USE_RUBBERBAND
4053         // Soundtouch uses percentage / 100 instead of normal (/ 1)
4054         if (_primary->region()->data_type() == DataType::AUDIO) {
4055                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
4056         }
4057 #endif
4058
4059         if (!_editor->get_selection().regions.empty()) {
4060                 /* primary will already be included in the selection, and edit
4061                    group shared editing will propagate selection across
4062                    equivalent regions, so just use the current region
4063                    selection.
4064                 */
4065
4066                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
4067                         error << _("An error occurred while executing time stretch operation") << endmsg;
4068                 }
4069         }
4070 }
4071
4072 void
4073 TimeFXDrag::aborted (bool)
4074 {
4075         _primary->get_time_axis_view().hide_timestretch ();
4076 }
4077
4078 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
4079         : Drag (e, i)
4080 {
4081         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
4082 }
4083
4084 void
4085 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4086 {
4087         Drag::start_grab (event);
4088 }
4089
4090 void
4091 ScrubDrag::motion (GdkEvent* /*event*/, bool)
4092 {
4093         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
4094 }
4095
4096 void
4097 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
4098 {
4099         if (movement_occurred && _editor->session()) {
4100                 /* make sure we stop */
4101                 _editor->session()->request_transport_speed (0.0);
4102         }
4103 }
4104
4105 void
4106 ScrubDrag::aborted (bool)
4107 {
4108         /* XXX: TODO */
4109 }
4110
4111 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4112         : Drag (e, i)
4113         , _operation (o)
4114         , _add (false)
4115         , _extend (false)
4116         , _original_pointer_time_axis (-1)
4117         , _last_pointer_time_axis (-1)
4118         , _time_selection_at_start (!_editor->get_selection().time.empty())
4119 {
4120         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4121         
4122         if (_time_selection_at_start) {
4123                 start_at_start = _editor->get_selection().time.start();
4124                 end_at_start = _editor->get_selection().time.end_frame();
4125         }
4126 }
4127
4128 void
4129 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4130 {
4131         if (_editor->session() == 0) {
4132                 return;
4133         }
4134
4135         Gdk::Cursor* cursor = 0;
4136
4137         switch (_operation) {
4138         case CreateSelection:
4139                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4140                         _add = true;
4141                 } else {
4142                         _add = false;
4143                 }
4144                 cursor = _editor->cursors()->selector;
4145                 Drag::start_grab (event, cursor);
4146                 break;
4147
4148         case SelectionStartTrim:
4149                 if (_editor->clicked_axisview) {
4150                         _editor->clicked_axisview->order_selection_trims (_item, true);
4151                 }
4152                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4153                 break;
4154
4155         case SelectionEndTrim:
4156                 if (_editor->clicked_axisview) {
4157                         _editor->clicked_axisview->order_selection_trims (_item, false);
4158                 }
4159                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4160                 break;
4161
4162         case SelectionMove:
4163                 Drag::start_grab (event, cursor);
4164                 break;
4165
4166         case SelectionExtend:
4167                 Drag::start_grab (event, cursor);
4168                 break;
4169         }
4170
4171         if (_operation == SelectionMove) {
4172                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4173         } else {
4174                 show_verbose_cursor_time (adjusted_current_frame (event));
4175         }
4176
4177         _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4178 }
4179
4180 void
4181 SelectionDrag::setup_pointer_frame_offset ()
4182 {
4183         switch (_operation) {
4184         case CreateSelection:
4185                 _pointer_frame_offset = 0;
4186                 break;
4187
4188         case SelectionStartTrim:
4189         case SelectionMove:
4190                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4191                 break;
4192
4193         case SelectionEndTrim:
4194                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4195                 break;
4196
4197         case SelectionExtend:
4198                 break;
4199         }
4200 }
4201
4202 void
4203 SelectionDrag::motion (GdkEvent* event, bool first_move)
4204 {
4205         framepos_t start = 0;
4206         framepos_t end = 0;
4207         framecnt_t length = 0;
4208         framecnt_t distance = 0;
4209
4210         framepos_t const pending_position = adjusted_current_frame (event);
4211
4212         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4213                 return;
4214         }
4215
4216         switch (_operation) {
4217         case CreateSelection:
4218         {
4219                 framepos_t grab = grab_frame ();
4220
4221                 if (first_move) {
4222                         grab = adjusted_current_frame (event, false);
4223                         if (grab < pending_position) {
4224                                 _editor->snap_to (grab, RoundDownMaybe);
4225                         }  else {
4226                                 _editor->snap_to (grab, RoundUpMaybe);
4227                         }
4228                 }
4229
4230                 if (pending_position < grab) {
4231                         start = pending_position;
4232                         end = grab;
4233                 } else {
4234                         end = pending_position;
4235                         start = grab;
4236                 }
4237
4238                 /* first drag: Either add to the selection
4239                    or create a new selection
4240                 */
4241
4242                 if (first_move) {
4243
4244                         if (_add) {
4245
4246                                 /* adding to the selection */
4247                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4248                                 _editor->clicked_selection = _editor->selection->add (start, end);
4249                                 _add = false;
4250                                 
4251                         } else {
4252
4253                                 /* new selection */
4254
4255                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4256                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4257                                 }
4258
4259                                 _editor->clicked_selection = _editor->selection->set (start, end);
4260                         }
4261                 }
4262                 
4263                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff, 
4264                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this 
4265                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4266                 if (atest) {
4267                         _editor->selection->add (atest);
4268                         break; 
4269                 }
4270                 
4271                 /* select all tracks within the rectangle that we've marked out so far */
4272                 TrackViewList new_selection;
4273                 TrackViewList& all_tracks (_editor->track_views);
4274
4275                 ArdourCanvas::Coord const top = grab_y();
4276                 ArdourCanvas::Coord const bottom = current_pointer_y();
4277
4278                 if (top >= 0 && bottom >= 0) {
4279
4280                         //first, find the tracks that are covered in the y range selection
4281                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4282                                 if ((*i)->covered_by_y_range (top, bottom)) {
4283                                         new_selection.push_back (*i);
4284                                 }
4285                         }
4286
4287                         //now find any tracks that are GROUPED with the tracks we selected
4288                         TrackViewList grouped_add = new_selection;
4289                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4290                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4291                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4292                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4293                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4294                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4295                                                         grouped_add.push_back (*j);
4296                                         }
4297                                 }
4298                         }
4299
4300                         //now compare our list with the current selection, and add or remove as necessary
4301                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4302                         TrackViewList tracks_to_add;
4303                         TrackViewList tracks_to_remove;
4304                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4305                                 if ( !_editor->selection->tracks.contains ( *i ) )
4306                                         tracks_to_add.push_back ( *i );
4307                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4308                                 if ( !grouped_add.contains ( *i ) )
4309                                         tracks_to_remove.push_back ( *i );
4310                         _editor->selection->add(tracks_to_add);
4311                         _editor->selection->remove(tracks_to_remove);
4312
4313                 }
4314         }
4315         break;
4316
4317         case SelectionStartTrim:
4318
4319                 start = _editor->selection->time[_editor->clicked_selection].start;
4320                 end = _editor->selection->time[_editor->clicked_selection].end;
4321
4322                 if (pending_position > end) {
4323                         start = end;
4324                 } else {
4325                         start = pending_position;
4326                 }
4327                 break;
4328
4329         case SelectionEndTrim:
4330
4331                 start = _editor->selection->time[_editor->clicked_selection].start;
4332                 end = _editor->selection->time[_editor->clicked_selection].end;
4333
4334                 if (pending_position < start) {
4335                         end = start;
4336                 } else {
4337                         end = pending_position;
4338                 }
4339
4340                 break;
4341                 
4342         case SelectionMove:
4343
4344                 start = _editor->selection->time[_editor->clicked_selection].start;
4345                 end = _editor->selection->time[_editor->clicked_selection].end;
4346
4347                 length = end - start;
4348                 distance = pending_position - start;
4349                 start = pending_position;
4350                 _editor->snap_to (start);
4351
4352                 end = start + length;
4353
4354                 break;
4355
4356         case SelectionExtend:
4357                 break;
4358         }
4359
4360         if (start != end) {
4361                 switch (_operation) {
4362                 case SelectionMove:     
4363                         if (_time_selection_at_start) {
4364                                 _editor->selection->move_time (distance);
4365                         }
4366                         break;
4367                 default:
4368                         _editor->selection->replace (_editor->clicked_selection, start, end);
4369                 }
4370         }
4371
4372         if (_operation == SelectionMove) {
4373                 show_verbose_cursor_time(start);
4374         } else {
4375                 show_verbose_cursor_time(pending_position);
4376         }
4377 }
4378
4379 void
4380 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4381 {
4382         Session* s = _editor->session();
4383
4384         if (movement_occurred) {
4385                 motion (event, false);
4386                 /* XXX this is not object-oriented programming at all. ick */
4387                 if (_editor->selection->time.consolidate()) {
4388                         _editor->selection->TimeChanged ();
4389                 }
4390
4391                 /* XXX what if its a music time selection? */
4392                 if (s) {
4393                         if ( s->get_play_range() && s->transport_rolling() ) {
4394                                 s->request_play_range (&_editor->selection->time, true);
4395                         } else {
4396                                 if (Config->get_follow_edits() && !s->transport_rolling()) {
4397                                         if (_operation == SelectionEndTrim)
4398                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4399                                         else
4400                                                 s->request_locate (_editor->get_selection().time.start());
4401                                 }
4402                         }
4403                 }
4404
4405         } else {
4406                 /* just a click, no pointer movement.
4407                  */
4408
4409                 if (_operation == SelectionExtend) {
4410                         if (_time_selection_at_start) {
4411                                 framepos_t pos = adjusted_current_frame (event, false);
4412                                 framepos_t start = min (pos, start_at_start);
4413                                 framepos_t end = max (pos, end_at_start);
4414                                 _editor->selection->set (start, end);
4415                         }
4416                 } else {
4417                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4418                                 if (_editor->clicked_selection) {
4419                                         _editor->selection->remove (_editor->clicked_selection);
4420                                 }
4421                         } else {
4422                                 if (!_editor->clicked_selection) {
4423                                         _editor->selection->clear_time();
4424                                 }
4425                         }
4426                 }
4427
4428                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4429                         _editor->selection->set (_editor->clicked_axisview);
4430                 }
4431                         
4432                 if (s && s->get_play_range () && s->transport_rolling()) {
4433                         s->request_stop (false, false);
4434                 }
4435
4436         }
4437
4438         _editor->stop_canvas_autoscroll ();
4439         _editor->clicked_selection = 0;
4440 }
4441
4442 void
4443 SelectionDrag::aborted (bool)
4444 {
4445         /* XXX: TODO */
4446 }
4447
4448 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4449         : Drag (e, i, false),
4450           _operation (o),
4451           _copy (false)
4452 {
4453         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4454
4455         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group, 
4456                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
4457                                                                       physical_screen_height (_editor->get_window())));
4458         _drag_rect->hide ();
4459
4460         _drag_rect->set_fill_color (ARDOUR_UI::config()->get_RangeDragRect());
4461         _drag_rect->set_outline_color (ARDOUR_UI::config()->get_RangeDragRect());
4462 }
4463
4464 void
4465 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4466 {
4467         if (_editor->session() == 0) {
4468                 return;
4469         }
4470
4471         Gdk::Cursor* cursor = 0;
4472
4473         if (!_editor->temp_location) {
4474                 _editor->temp_location = new Location (*_editor->session());
4475         }
4476
4477         switch (_operation) {
4478         case CreateSkipMarker:
4479         case CreateRangeMarker:
4480         case CreateTransportMarker:
4481         case CreateCDMarker:
4482
4483                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4484                         _copy = true;
4485                 } else {
4486                         _copy = false;
4487                 }
4488                 cursor = _editor->cursors()->selector;
4489                 break;
4490         }
4491
4492         Drag::start_grab (event, cursor);
4493
4494         show_verbose_cursor_time (adjusted_current_frame (event));
4495 }
4496
4497 void
4498 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4499 {
4500         framepos_t start = 0;
4501         framepos_t end = 0;
4502         ArdourCanvas::Rectangle *crect;
4503
4504         switch (_operation) {
4505         case CreateSkipMarker:
4506                 crect = _editor->range_bar_drag_rect;
4507                 break;
4508         case CreateRangeMarker:
4509                 crect = _editor->range_bar_drag_rect;
4510                 break;
4511         case CreateTransportMarker:
4512                 crect = _editor->transport_bar_drag_rect;
4513                 break;
4514         case CreateCDMarker:
4515                 crect = _editor->cd_marker_bar_drag_rect;
4516                 break;
4517         default:
4518                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4519                 return;
4520                 break;
4521         }
4522
4523         framepos_t const pf = adjusted_current_frame (event);
4524
4525         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4526                 framepos_t grab = grab_frame ();
4527                 _editor->snap_to (grab);
4528
4529                 if (pf < grab_frame()) {
4530                         start = pf;
4531                         end = grab;
4532                 } else {
4533                         end = pf;
4534                         start = grab;
4535                 }
4536
4537                 /* first drag: Either add to the selection
4538                    or create a new selection.
4539                 */
4540
4541                 if (first_move) {
4542
4543                         _editor->temp_location->set (start, end);
4544
4545                         crect->show ();
4546
4547                         update_item (_editor->temp_location);
4548                         _drag_rect->show();
4549                         //_drag_rect->raise_to_top();
4550
4551                 }
4552         }
4553
4554         if (start != end) {
4555                 _editor->temp_location->set (start, end);
4556
4557                 double x1 = _editor->sample_to_pixel (start);
4558                 double x2 = _editor->sample_to_pixel (end);
4559                 crect->set_x0 (x1);
4560                 crect->set_x1 (x2);
4561
4562                 update_item (_editor->temp_location);
4563         }
4564
4565         show_verbose_cursor_time (pf);
4566
4567 }
4568
4569 void
4570 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4571 {
4572         Location * newloc = 0;
4573         string rangename;
4574         int flags;
4575
4576         if (movement_occurred) {
4577                 motion (event, false);
4578                 _drag_rect->hide();
4579
4580                 switch (_operation) {
4581                 case CreateSkipMarker:
4582                 case CreateRangeMarker:
4583                 case CreateCDMarker:
4584                     {
4585                         XMLNode &before = _editor->session()->locations()->get_state();
4586                         if (_operation == CreateSkipMarker) {
4587                                 _editor->begin_reversible_command (_("new skip marker"));
4588                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4589                                 flags = Location::IsRangeMarker | Location::IsSkip;
4590                                 _editor->range_bar_drag_rect->hide();
4591                         } else if (_operation == CreateCDMarker) {
4592                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4593                                 _editor->begin_reversible_command (_("new CD marker"));
4594                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
4595                                 _editor->cd_marker_bar_drag_rect->hide();
4596                         } else {
4597                                 _editor->begin_reversible_command (_("new skip marker"));
4598                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4599                                 flags = Location::IsRangeMarker;
4600                                 _editor->range_bar_drag_rect->hide();
4601                         }
4602                         newloc = new Location (
4603                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4604                                 );
4605
4606                         _editor->session()->locations()->add (newloc, true);
4607                         XMLNode &after = _editor->session()->locations()->get_state();
4608                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4609                         _editor->commit_reversible_command ();
4610                         break;
4611                     }
4612
4613                 case CreateTransportMarker:
4614                         // popup menu to pick loop or punch
4615                         _editor->new_transport_marker_context_menu (&event->button, _item);
4616                         break;
4617                 }
4618
4619         } else {
4620
4621                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4622
4623                 if (_operation == CreateTransportMarker) {
4624
4625                         /* didn't drag, so just locate */
4626
4627                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4628
4629                 } else if (_operation == CreateCDMarker) {
4630
4631                         /* didn't drag, but mark is already created so do
4632                          * nothing */
4633
4634                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4635                         
4636
4637                         framepos_t start;
4638                         framepos_t end;
4639
4640                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4641
4642                         if (end == max_framepos) {
4643                                 end = _editor->session()->current_end_frame ();
4644                         }
4645
4646                         if (start == max_framepos) {
4647                                 start = _editor->session()->current_start_frame ();
4648                         }
4649
4650                         switch (_editor->mouse_mode) {
4651                         case MouseObject:
4652                                 /* find the two markers on either side and then make the selection from it */
4653                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4654                                 break;
4655
4656                         case MouseRange:
4657                                 /* find the two markers on either side of the click and make the range out of it */
4658                                 _editor->selection->set (start, end);
4659                                 break;
4660
4661                         default:
4662                                 break;
4663                         }
4664                 }
4665         }
4666
4667         _editor->stop_canvas_autoscroll ();
4668 }
4669
4670 void
4671 RangeMarkerBarDrag::aborted (bool)
4672 {
4673         /* XXX: TODO */
4674 }
4675
4676 void
4677 RangeMarkerBarDrag::update_item (Location* location)
4678 {
4679         double const x1 = _editor->sample_to_pixel (location->start());
4680         double const x2 = _editor->sample_to_pixel (location->end());
4681
4682         _drag_rect->set_x0 (x1);
4683         _drag_rect->set_x1 (x2);
4684 }
4685
4686 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4687         : Drag (e, i)
4688         , _cumulative_dx (0)
4689         , _cumulative_dy (0)
4690 {
4691         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4692
4693         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4694         assert (_primary);
4695         _region = &_primary->region_view ();
4696         _note_height = _region->midi_stream_view()->note_height ();
4697 }
4698
4699 void
4700 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4701 {
4702         Drag::start_grab (event);
4703
4704         if (!(_was_selected = _primary->selected())) {
4705
4706                 /* tertiary-click means extend selection - we'll do that on button release,
4707                    so don't add it here, because otherwise we make it hard to figure
4708                    out the "extend-to" range.
4709                 */
4710
4711                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4712
4713                 if (!extend) {
4714                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4715
4716                         if (add) {
4717                                 _region->note_selected (_primary, true);
4718                         } else {
4719                                 _region->unique_select (_primary);
4720                         }
4721                 }
4722         }
4723 }
4724
4725 /** @return Current total drag x change in frames */
4726 frameoffset_t
4727 NoteDrag::total_dx () const
4728 {
4729         /* dx in frames */
4730         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4731
4732         /* primary note time */
4733         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4734
4735         /* new time of the primary note in session frames */
4736         frameoffset_t st = n + dx;
4737
4738         framepos_t const rp = _region->region()->position ();
4739
4740         /* prevent the note being dragged earlier than the region's position */
4741         st = max (st, rp);
4742
4743         /* snap and return corresponding delta */
4744         return _region->snap_frame_to_frame (st - rp) + rp - n;
4745 }
4746
4747 /** @return Current total drag y change in note number */
4748 int8_t
4749 NoteDrag::total_dy () const
4750 {
4751         MidiStreamView* msv = _region->midi_stream_view ();
4752         double const y = _region->midi_view()->y_position ();
4753         /* new current note */
4754         uint8_t n = msv->y_to_note (current_pointer_y () - y);
4755         /* clamp */
4756         n = max (msv->lowest_note(), n);
4757         n = min (msv->highest_note(), n);
4758         /* and work out delta */
4759         return n - msv->y_to_note (grab_y() - y);
4760 }
4761
4762 void
4763 NoteDrag::motion (GdkEvent *, bool)
4764 {
4765         /* Total change in x and y since the start of the drag */
4766         frameoffset_t const dx = total_dx ();
4767         int8_t const dy = total_dy ();
4768
4769         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4770         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4771         double const tdy = -dy * _note_height - _cumulative_dy;
4772
4773         if (tdx || tdy) {
4774                 _cumulative_dx += tdx;
4775                 _cumulative_dy += tdy;
4776
4777                 int8_t note_delta = total_dy();
4778
4779                 _region->move_selection (tdx, tdy, note_delta);
4780
4781                 /* the new note value may be the same as the old one, but we
4782                  * don't know what that means because the selection may have
4783                  * involved more than one note and we might be doing something
4784                  * odd with them. so show the note value anyway, always.
4785                  */
4786
4787                 char buf[12];
4788                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4789                 
4790                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4791                           (int) floor ((double)new_note));
4792
4793                 show_verbose_cursor_text (buf);
4794         }
4795 }
4796
4797 void
4798 NoteDrag::finished (GdkEvent* ev, bool moved)
4799 {
4800         if (!moved) {
4801                 /* no motion - select note */
4802                 
4803                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4804                     _editor->current_mouse_mode() == Editing::MouseDraw) {
4805                         
4806                         if (_was_selected) {
4807                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4808                                 if (add) {
4809                                         _region->note_deselected (_primary);
4810                                 }
4811                         } else {
4812                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4813                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4814
4815                                 if (!extend && !add && _region->selection_size() > 1) {
4816                                         _region->unique_select (_primary);
4817                                 } else if (extend) {
4818                                         _region->note_selected (_primary, true, true);
4819                                 } else {
4820                                         /* it was added during button press */
4821                                 }
4822                         }
4823                 }
4824         } else {
4825                 _region->note_dropped (_primary, total_dx(), total_dy());
4826         }
4827 }
4828
4829 void
4830 NoteDrag::aborted (bool)
4831 {
4832         /* XXX: TODO */
4833 }
4834
4835 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4836 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4837         : Drag (editor, atv->base_item ())
4838         , _ranges (r)
4839         , _nothing_to_drag (false)
4840 {
4841         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4842         y_origin = atv->y_position();
4843         setup (atv->lines ());
4844 }
4845
4846 /** Make an AutomationRangeDrag for region gain lines */
4847 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4848         : Drag (editor, rv->get_canvas_group ())
4849         , _ranges (r)
4850         , _nothing_to_drag (false)
4851 {
4852         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4853
4854         list<boost::shared_ptr<AutomationLine> > lines;
4855         lines.push_back (rv->get_gain_line ());
4856         y_origin = rv->get_time_axis_view().y_position();
4857         setup (lines);
4858 }
4859
4860 /** @param lines AutomationLines to drag.
4861  *  @param offset Offset from the session start to the points in the AutomationLines.
4862  */
4863 void
4864 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4865 {
4866         /* find the lines that overlap the ranges being dragged */
4867         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4868         while (i != lines.end ()) {
4869                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4870                 ++j;
4871
4872                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4873
4874                 /* check this range against all the AudioRanges that we are using */
4875                 list<AudioRange>::const_iterator k = _ranges.begin ();
4876                 while (k != _ranges.end()) {
4877                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4878                                 break;
4879                         }
4880                         ++k;
4881                 }
4882
4883                 /* add it to our list if it overlaps at all */
4884                 if (k != _ranges.end()) {
4885                         Line n;
4886                         n.line = *i;
4887                         n.state = 0;
4888                         n.range = r;
4889                         _lines.push_back (n);
4890                 }
4891
4892                 i = j;
4893         }
4894
4895         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4896 }
4897
4898 double
4899 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4900 {
4901         return 1.0 - ((global_y - y_origin) / line->height());
4902 }
4903
4904 void
4905 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4906 {
4907         Drag::start_grab (event, cursor);
4908
4909         /* Get line states before we start changing things */
4910         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4911                 i->state = &i->line->get_state ();
4912                 i->original_fraction = y_fraction (i->line, current_pointer_y());
4913         }
4914
4915         if (_ranges.empty()) {
4916
4917                 /* No selected time ranges: drag all points */
4918                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4919                         uint32_t const N = i->line->npoints ();
4920                         for (uint32_t j = 0; j < N; ++j) {
4921                                 i->points.push_back (i->line->nth (j));
4922                         }
4923                 }
4924
4925         } else {
4926
4927                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4928
4929                         framecnt_t const half = (i->start + i->end) / 2;
4930
4931                         /* find the line that this audio range starts in */
4932                         list<Line>::iterator j = _lines.begin();
4933                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4934                                 ++j;
4935                         }
4936
4937                         if (j != _lines.end()) {
4938                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4939
4940                                 /* j is the line that this audio range starts in; fade into it;
4941                                    64 samples length plucked out of thin air.
4942                                 */
4943
4944                                 framepos_t a = i->start + 64;
4945                                 if (a > half) {
4946                                         a = half;
4947                                 }
4948
4949                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4950                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4951
4952                                 the_list->editor_add (p, the_list->eval (p));
4953                                 the_list->editor_add (q, the_list->eval (q));
4954                         }
4955
4956                         /* same thing for the end */
4957
4958                         j = _lines.begin();
4959                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4960                                 ++j;
4961                         }
4962
4963                         if (j != _lines.end()) {
4964                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4965
4966                                 /* j is the line that this audio range starts in; fade out of it;
4967                                    64 samples length plucked out of thin air.
4968                                 */
4969
4970                                 framepos_t b = i->end - 64;
4971                                 if (b < half) {
4972                                         b = half;
4973                                 }
4974
4975                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4976                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4977
4978                                 the_list->editor_add (p, the_list->eval (p));
4979                                 the_list->editor_add (q, the_list->eval (q));
4980                         }
4981                 }
4982
4983                 _nothing_to_drag = true;
4984
4985                 /* Find all the points that should be dragged and put them in the relevant
4986                    points lists in the Line structs.
4987                 */
4988
4989                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4990
4991                         uint32_t const N = i->line->npoints ();
4992                         for (uint32_t j = 0; j < N; ++j) {
4993
4994                                 /* here's a control point on this line */
4995                                 ControlPoint* p = i->line->nth (j);
4996                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4997
4998                                 /* see if it's inside a range */
4999                                 list<AudioRange>::const_iterator k = _ranges.begin ();
5000                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
5001                                         ++k;
5002                                 }
5003
5004                                 if (k != _ranges.end()) {
5005                                         /* dragging this point */
5006                                         _nothing_to_drag = false;
5007                                         i->points.push_back (p);
5008                                 }
5009                         }
5010                 }
5011         }
5012
5013         if (_nothing_to_drag) {
5014                 return;
5015         }
5016
5017         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5018                 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5019         }
5020 }
5021
5022 void
5023 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5024 {
5025         if (_nothing_to_drag) {
5026                 return;
5027         }
5028
5029         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5030                 float const f = y_fraction (l->line, current_pointer_y());
5031                 /* we are ignoring x position for this drag, so we can just pass in anything */
5032                 uint32_t ignored;
5033                 l->line->drag_motion (0, f, true, false, ignored);
5034                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5035         }
5036 }
5037
5038 void
5039 AutomationRangeDrag::finished (GdkEvent* event, bool)
5040 {
5041         if (_nothing_to_drag) {
5042                 return;
5043         }
5044
5045         motion (event, false);
5046         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5047                 i->line->end_drag (false, 0);
5048         }
5049
5050         _editor->session()->commit_reversible_command ();
5051 }
5052
5053 void
5054 AutomationRangeDrag::aborted (bool)
5055 {
5056         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5057                 i->line->reset ();
5058         }
5059 }
5060
5061 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5062         : view (v)
5063         , initial_time_axis_view (itav)
5064 {
5065         /* note that time_axis_view may be null if the regionview was created
5066          * as part of a copy operation.
5067          */
5068         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5069         layer = v->region()->layer ();
5070         initial_y = v->get_canvas_group()->position().y;
5071         initial_playlist = v->region()->playlist ();
5072         initial_position = v->region()->position ();
5073         initial_end = v->region()->position () + v->region()->length ();
5074 }
5075
5076 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5077         : Drag (e, i->canvas_item ())
5078         , _region_view (r)
5079         , _patch_change (i)
5080         , _cumulative_dx (0)
5081 {
5082         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5083                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5084                                                    grab_frame()));
5085 }
5086
5087 void
5088 PatchChangeDrag::motion (GdkEvent* ev, bool)
5089 {
5090         framepos_t f = adjusted_current_frame (ev);
5091         boost::shared_ptr<Region> r = _region_view->region ();
5092         f = max (f, r->position ());
5093         f = min (f, r->last_frame ());
5094
5095         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5096         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5097         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5098         _cumulative_dx = dxu;
5099 }
5100
5101 void
5102 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5103 {
5104         if (!movement_occurred) {
5105                 return;
5106         }
5107
5108         boost::shared_ptr<Region> r (_region_view->region ());
5109         framepos_t f = adjusted_current_frame (ev);
5110         f = max (f, r->position ());
5111         f = min (f, r->last_frame ());
5112
5113         _region_view->move_patch_change (
5114                 *_patch_change,
5115                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5116                 );
5117 }
5118
5119 void
5120 PatchChangeDrag::aborted (bool)
5121 {
5122         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5123 }
5124
5125 void
5126 PatchChangeDrag::setup_pointer_frame_offset ()
5127 {
5128         boost::shared_ptr<Region> region = _region_view->region ();
5129         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5130 }
5131
5132 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5133         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5134         , _region_view (rv)
5135 {
5136
5137 }
5138
5139 void
5140 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5141 {
5142         framepos_t const p = _region_view->region()->position ();
5143         double const y = _region_view->midi_view()->y_position ();
5144
5145         x1 = max ((framepos_t) 0, x1 - p);
5146         x2 = max ((framepos_t) 0, x2 - p);
5147         y1 = max (0.0, y1 - y);
5148         y2 = max (0.0, y2 - y);
5149         
5150         _region_view->update_drag_selection (
5151                 _editor->sample_to_pixel (x1),
5152                 _editor->sample_to_pixel (x2),
5153                 y1,
5154                 y2,
5155                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5156                 );
5157 }
5158
5159 void
5160 MidiRubberbandSelectDrag::deselect_things ()
5161 {
5162         /* XXX */
5163 }
5164
5165 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5166         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5167         , _region_view (rv)
5168 {
5169         _vertical_only = true;
5170 }
5171
5172 void
5173 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5174 {
5175         double const y = _region_view->midi_view()->y_position ();
5176
5177         y1 = max (0.0, y1 - y);
5178         y2 = max (0.0, y2 - y);
5179         
5180         _region_view->update_vertical_drag_selection (
5181                 y1,
5182                 y2,
5183                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5184                 );
5185 }
5186
5187 void
5188 MidiVerticalSelectDrag::deselect_things ()
5189 {
5190         /* XXX */
5191 }
5192
5193 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5194         : RubberbandSelectDrag (e, i)
5195 {
5196
5197 }
5198
5199 void
5200 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5201 {
5202         if (drag_in_progress) {
5203                 /* We just want to select things at the end of the drag, not during it */
5204                 return;
5205         }
5206         
5207         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5208         
5209         _editor->begin_reversible_command (_("rubberband selection"));
5210         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5211         _editor->commit_reversible_command ();
5212 }
5213
5214 void
5215 EditorRubberbandSelectDrag::deselect_things ()
5216 {
5217         if (!getenv("ARDOUR_SAE")) {
5218                 _editor->selection->clear_tracks();
5219         }
5220         _editor->selection->clear_regions();
5221         _editor->selection->clear_points ();
5222         _editor->selection->clear_lines ();
5223 }
5224
5225 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5226         : Drag (e, i)
5227         , _region_view (rv)
5228         , _drag_rect (0)
5229 {
5230         
5231 }
5232
5233 NoteCreateDrag::~NoteCreateDrag ()
5234 {
5235         delete _drag_rect;
5236 }
5237
5238 framecnt_t
5239 NoteCreateDrag::grid_frames (framepos_t t) const
5240 {
5241         bool success;
5242         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5243         if (!success) {
5244                 grid_beats = 1;
5245         }
5246
5247         return _region_view->region_beats_to_region_frames (grid_beats);
5248 }
5249
5250 void
5251 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5252 {
5253         Drag::start_grab (event, cursor);
5254                                                  
5255         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5256
5257         framepos_t pf = _drags->current_pointer_frame ();
5258         framecnt_t const g = grid_frames (pf);
5259
5260         /* Hack so that we always snap to the note that we are over, instead of snapping
5261            to the next one if we're more than halfway through the one we're over.
5262         */
5263         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5264                 pf -= g / 2;
5265         }
5266
5267         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5268
5269         MidiStreamView* sv = _region_view->midi_stream_view ();
5270         double const x = _editor->sample_to_pixel (_note[0]);
5271         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5272
5273         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5274         _drag_rect->set_outline_all ();
5275         _drag_rect->set_outline_color (0xffffff99);
5276         _drag_rect->set_fill_color (0xffffff66);
5277 }
5278
5279 void
5280 NoteCreateDrag::motion (GdkEvent* event, bool)
5281 {
5282         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5283         double const x0 = _editor->sample_to_pixel (_note[0]);
5284         double const x1 = _editor->sample_to_pixel (_note[1]);
5285         _drag_rect->set_x0 (std::min(x0, x1));
5286         _drag_rect->set_x1 (std::max(x0, x1));
5287 }
5288
5289 void
5290 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5291 {
5292         if (!had_movement) {
5293                 return;
5294         }
5295         
5296         framepos_t const start = min (_note[0], _note[1]);
5297         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5298
5299         framecnt_t const g = grid_frames (start);
5300         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
5301         
5302         if (_editor->snap_mode() == SnapNormal && length < g) {
5303                 length = g - one_tick;
5304         }
5305
5306         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
5307
5308         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5309 }
5310
5311 double
5312 NoteCreateDrag::y_to_region (double y) const
5313 {
5314         double x = 0;
5315         _region_view->get_canvas_group()->canvas_to_item (x, y);
5316         return y;
5317 }
5318
5319 void
5320 NoteCreateDrag::aborted (bool)
5321 {
5322         
5323 }
5324
5325 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5326         : Drag (e, i)
5327         , arv (rv)
5328         , start (start_yn)
5329 {
5330         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5331 }
5332
5333 void
5334 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5335 {
5336         Drag::start_grab (event, cursor);
5337 }
5338
5339 void
5340 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5341 {
5342         double distance;
5343         double new_length;
5344         framecnt_t len;
5345
5346         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5347
5348         if (start) {
5349                 distance = _drags->current_pointer_x() - grab_x();
5350                 len = ar->fade_in()->back()->when;
5351         } else {
5352                 distance = grab_x() - _drags->current_pointer_x();
5353                 len = ar->fade_out()->back()->when;
5354         }
5355
5356         /* how long should it be ? */
5357
5358         new_length = len + _editor->pixel_to_sample (distance);
5359
5360         /* now check with the region that this is legal */
5361
5362         new_length = ar->verify_xfade_bounds (new_length, start);
5363
5364         if (start) {
5365                 arv->reset_fade_in_shape_width (ar, new_length);
5366         } else {
5367                 arv->reset_fade_out_shape_width (ar, new_length);
5368         }
5369 }
5370
5371 void
5372 CrossfadeEdgeDrag::finished (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         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5389         
5390         _editor->begin_reversible_command ("xfade trim");
5391         ar->playlist()->clear_owned_changes (); 
5392
5393         if (start) {
5394                 ar->set_fade_in_length (new_length);
5395         } else {
5396                 ar->set_fade_out_length (new_length);
5397         }
5398
5399         /* Adjusting the xfade may affect other regions in the playlist, so we need
5400            to get undo Commands from the whole playlist rather than just the
5401            region.
5402         */
5403
5404         vector<Command*> cmds;
5405         ar->playlist()->rdiff (cmds);
5406         _editor->session()->add_commands (cmds);
5407         _editor->commit_reversible_command ();
5408
5409 }
5410
5411 void
5412 CrossfadeEdgeDrag::aborted (bool)
5413 {
5414         if (start) {
5415                 arv->redraw_start_xfade ();
5416         } else {
5417                 arv->redraw_end_xfade ();
5418         }
5419 }
5420
5421 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5422         : Drag (e, item, true)
5423         , line (new EditorCursor (*e))
5424 {
5425         line->set_position (pos);
5426         line->show ();
5427 }
5428
5429 RegionCutDrag::~RegionCutDrag ()
5430 {
5431         delete line;
5432 }
5433
5434 void
5435 RegionCutDrag::motion (GdkEvent*, bool)
5436 {
5437         framepos_t where = _drags->current_pointer_frame();
5438         _editor->snap_to (where);
5439
5440         line->set_position (where);
5441 }
5442
5443 void
5444 RegionCutDrag::finished (GdkEvent*, bool)
5445 {
5446         _editor->get_track_canvas()->canvas()->re_enter();
5447
5448         framepos_t pos = _drags->current_pointer_frame();
5449         
5450         line->hide ();
5451
5452         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5453
5454         if (rs.empty()) {
5455                 return;
5456         }
5457
5458         _editor->split_regions_at (pos, rs);
5459 }
5460
5461 void
5462 RegionCutDrag::aborted (bool)
5463 {
5464 }