Fix various cursor problems.
[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                 _cursor_ctx = CursorContext::create(*_editor, 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         _cursor_ctx.reset();
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_x() const
343 {
344         return _drags->current_pointer_x ();
345 }
346
347 double
348 Drag::current_pointer_y () const
349 {
350         if (!_trackview_only) {
351                 return _drags->current_pointer_y ();
352         }
353         
354         return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
355 }
356
357 bool
358 Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
359 {
360         /* check to see if we have moved in any way that matters since the last motion event */
361         if (_move_threshold_passed &&
362             (!x_movement_matters() || _last_pointer_x == current_pointer_x ()) &&
363             (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
364                 return false;
365         }
366
367         pair<framecnt_t, int> const threshold = move_threshold ();
368
369         bool const old_move_threshold_passed = _move_threshold_passed;
370
371         if (!from_autoscroll && !_move_threshold_passed) {
372
373                 bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
374                 bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
375
376                 _move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
377         }
378
379         if (active (_editor->mouse_mode) && _move_threshold_passed) {
380
381                 if (event->motion.state & Gdk::BUTTON1_MASK || event->motion.state & Gdk::BUTTON2_MASK) {
382                         if (!from_autoscroll) {
383                                 _editor->maybe_autoscroll (true, allow_vertical_autoscroll (), false);
384                         }
385
386                         if (!_editor->autoscroll_active() || from_autoscroll) {
387                                 motion (event, _move_threshold_passed != old_move_threshold_passed);
388                                 
389                                 _last_pointer_x = _drags->current_pointer_x ();
390                                 _last_pointer_y = current_pointer_y ();
391                                 _last_pointer_frame = adjusted_current_frame (event);
392                         }
393
394                         return true;
395                 }
396         }
397
398         return false;
399 }
400
401 /** Call to abort a drag.  Ungrabs item and calls subclass's aborted () */
402 void
403 Drag::abort ()
404 {
405         if (_item) {
406                 _item->ungrab ();
407         }
408
409         aborted (_move_threshold_passed);
410
411         _editor->stop_canvas_autoscroll ();
412         _editor->verbose_cursor()->hide ();
413 }
414
415 void
416 Drag::show_verbose_cursor_time (framepos_t frame)
417 {
418         _editor->verbose_cursor()->set_time (frame);
419         _editor->verbose_cursor()->show ();
420 }
421
422 void
423 Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
424 {
425         _editor->verbose_cursor()->set_duration (start, end);
426         _editor->verbose_cursor()->show ();
427 }
428
429 void
430 Drag::show_verbose_cursor_text (string const & text)
431 {
432         _editor->verbose_cursor()->set (text);
433         _editor->verbose_cursor()->show ();
434 }
435
436 boost::shared_ptr<Region>
437 Drag::add_midi_region (MidiTimeAxisView* view)
438 {
439         if (_editor->session()) {
440                 const TempoMap& map (_editor->session()->tempo_map());
441                 framecnt_t pos = grab_frame();
442                 const Meter& m = map.meter_at (pos);
443                 /* not that the frame rate used here can be affected by pull up/down which
444                    might be wrong.
445                 */
446                 framecnt_t len = m.frames_per_bar (map.tempo_at (pos), _editor->session()->frame_rate());
447                 return view->add_region (grab_frame(), len, true);
448         }
449
450         return boost::shared_ptr<Region>();
451 }
452
453 struct EditorOrderTimeAxisViewSorter {
454     bool operator() (TimeAxisView* a, TimeAxisView* b) {
455             RouteTimeAxisView* ra = dynamic_cast<RouteTimeAxisView*> (a);
456             RouteTimeAxisView* rb = dynamic_cast<RouteTimeAxisView*> (b);
457             assert (ra && rb);
458             return ra->route()->order_key () < rb->route()->order_key ();
459     }
460 };
461
462 RegionDrag::RegionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
463         : Drag (e, i),
464           _primary (p)
465 {
466         _editor->visible_order_range (&_visible_y_low, &_visible_y_high);
467
468         /* Make a list of tracks to refer to during the drag; we include hidden tracks,
469            as some of the regions we are dragging may be on such tracks.
470         */
471
472         TrackViewList track_views = _editor->track_views;
473         track_views.sort (EditorOrderTimeAxisViewSorter ());
474
475         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
476                 _time_axis_views.push_back (*i);
477                 
478                 TimeAxisView::Children children_list = (*i)->get_child_list ();
479                 for (TimeAxisView::Children::iterator j = children_list.begin(); j != children_list.end(); ++j) {
480                         _time_axis_views.push_back (j->get());
481                 }
482         }
483
484         /* the list of views can be empty at this point if this is a region list-insert drag
485          */
486
487         for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
488                 _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
489         }
490
491         RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
492 }
493
494 void
495 RegionDrag::region_going_away (RegionView* v)
496 {
497         list<DraggingView>::iterator i = _views.begin ();
498         while (i != _views.end() && i->view != v) {
499                 ++i;
500         }
501
502         if (i != _views.end()) {
503                 _views.erase (i);
504         }
505 }
506
507 /** Given a TimeAxisView, return the index of it into the _time_axis_views vector,
508  *  or -1 if it is not found.
509  */
510 int
511 RegionDrag::find_time_axis_view (TimeAxisView* t) const
512 {
513         int i = 0;
514         int const N = _time_axis_views.size ();
515         while (i < N && _time_axis_views[i] != t) {
516                 ++i;
517         }
518
519         if (i == N) {
520                 return -1;
521         }
522
523         return i;
524 }
525
526 RegionMotionDrag::RegionMotionDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b)
527         : RegionDrag (e, i, p, v)
528         , _brushing (b)
529         , _total_x_delta (0)
530         , _last_pointer_time_axis_view (0)
531         , _last_pointer_layer (0)
532 {
533         DEBUG_TRACE (DEBUG::Drags, "New RegionMotionDrag\n");
534 }
535
536 void
537 RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
538 {
539         Drag::start_grab (event, cursor);
540
541         show_verbose_cursor_time (_last_frame_position);
542
543         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
544         if (tv.first) {
545                 _last_pointer_time_axis_view = find_time_axis_view (tv.first);
546                 _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
547         }
548 }
549
550 double
551 RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_region_position)
552 {
553         /* compute the amount of pointer motion in frames, and where
554            the region would be if we moved it by that much.
555         */
556         *pending_region_position = adjusted_current_frame (event);
557
558         framepos_t sync_frame;
559         framecnt_t sync_offset;
560         int32_t sync_dir;
561
562         sync_offset = _primary->region()->sync_offset (sync_dir);
563
564         /* we don't handle a sync point that lies before zero.
565          */
566         if (sync_dir >= 0 || (sync_dir < 0 && *pending_region_position >= sync_offset)) {
567
568                 sync_frame = *pending_region_position + (sync_dir*sync_offset);
569
570                 _editor->snap_to_with_modifier (sync_frame, event);
571
572                 *pending_region_position = _primary->region()->adjust_to_sync (sync_frame);
573
574         } else {
575                 *pending_region_position = _last_frame_position;
576         }
577
578         if (*pending_region_position > max_framepos - _primary->region()->length()) {
579                 *pending_region_position = _last_frame_position;
580         }
581
582         double dx = 0;
583
584         /* in locked edit mode, reverse the usual meaning of _x_constrained */
585         bool const x_move_allowed = Config->get_edit_mode() == Lock ? _x_constrained : !_x_constrained;
586
587         if ((*pending_region_position != _last_frame_position) && x_move_allowed) {
588
589                 /* x movement since last time (in pixels) */
590                 dx = (static_cast<double> (*pending_region_position) - _last_frame_position) / _editor->samples_per_pixel;
591
592                 /* total x movement */
593                 framecnt_t total_dx = *pending_region_position;
594                 if (regions_came_from_canvas()) {
595                         total_dx = total_dx - grab_frame ();
596                 }
597
598                 /* check that no regions have gone off the start of the session */
599                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
600                         if ((i->view->region()->position() + total_dx) < 0) {
601                                 dx = 0;
602                                 *pending_region_position = _last_frame_position;
603                                 break;
604                         }
605                 }
606
607         }
608
609         return dx;
610 }
611
612 bool
613 RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
614 {
615         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
616                 int const n = i->time_axis_view + delta_track;
617                 if (n < 0 || n >= int (_time_axis_views.size())) {
618                         /* off the top or bottom track */
619                         return false;
620                 }
621
622                 RouteTimeAxisView const * to = dynamic_cast<RouteTimeAxisView const *> (_time_axis_views[n]);
623                 if (to == 0 || !to->is_track() || to->track()->data_type() != i->view->region()->data_type()) {
624                         /* not a track, or the wrong type */
625                         return false;
626                 }
627
628                 double const l = i->layer + delta_layer;
629
630                 /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
631                    mode to allow the user to place a region below another on layer 0.
632                 */
633                 if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
634                         /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
635                            If it has, the layers will be munged later anyway, so it's ok.
636                         */
637                         return false;
638                 }
639         }
640
641         /* all regions being dragged are ok with this change */
642         return true;
643 }
644
645 void
646 RegionMotionDrag::motion (GdkEvent* event, bool first_move)
647 {
648         double delta_layer = 0;
649         int delta_time_axis_view = 0;
650
651         assert (!_views.empty ());
652
653         /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
654
655         /* Find the TimeAxisView that the pointer is now over */
656         pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
657         TimeAxisView* tv = r.first;
658
659         if (tv && tv->view()) {
660                 double layer = r.second;
661
662                 if (first_move && tv->view()->layer_display() == Stacked) {
663                         tv->view()->set_layer_display (Expanded);
664                 }
665
666                 /* Here's the current pointer position in terms of time axis view and layer */
667                 int const current_pointer_time_axis_view = find_time_axis_view (tv);
668                 double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
669                 
670                 /* Work out the change in y */
671
672                 delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
673                 delta_layer = current_pointer_layer - _last_pointer_layer;
674         } 
675         
676         /* Work out the change in x */
677         framepos_t pending_region_position;
678         double const x_delta = compute_x_delta (event, &pending_region_position);
679         _last_frame_position = pending_region_position;
680
681         /* Verify change in y */
682         if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
683                 /* this y movement is not allowed, so do no y movement this time */
684                 delta_time_axis_view = 0;
685                 delta_layer = 0;
686         }
687
688         if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
689                 /* haven't reached next snap point, and we're not switching
690                    trackviews nor layers. nothing to do.
691                 */
692                 return;
693         }
694
695         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
696
697         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
698
699                 RegionView* rv = i->view;
700
701                 if (rv->region()->locked() || rv->region()->video_locked()) {
702                         continue;
703                 }
704
705                 if (first_move) {
706                         rv->drag_start (); 
707
708                         /* reparent the regionview into a group above all
709                          * others
710                          */
711                         
712                         ArdourCanvas::Item* rvg = rv->get_canvas_group();
713                         Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
714                         Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
715                         rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
716                         /* move the item so that it continues to appear at the
717                            same location now that its parent has changed.
718                         */
719                         rvg->move (rv_canvas_offset - dmg_canvas_offset);
720                 }
721
722                 /* If we have moved tracks, we'll fudge the layer delta so that the
723                    region gets moved back onto layer 0 on its new track; this avoids
724                    confusion when dragging regions from non-zero layers onto different
725                    tracks.
726                 */
727                 double this_delta_layer = delta_layer;
728                 if (delta_time_axis_view != 0) {
729                         this_delta_layer = - i->layer;
730                 }
731
732                 if (tv) {
733
734                         int track_index;
735                         
736                         if (i->time_axis_view >= 0) {
737                                 track_index = i->time_axis_view + delta_time_axis_view;
738                         } else {
739                                 track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
740                         }
741                          
742                         if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
743                                 continue;
744                         }
745
746                         /* The TimeAxisView that this region is now over */
747                         TimeAxisView* current_tv = _time_axis_views[track_index];
748
749                         /* Ensure it is moved from stacked -> expanded if appropriate */
750                         if (current_tv->view()->layer_display() == Stacked) {
751                                 current_tv->view()->set_layer_display (Expanded);
752                         }
753                 
754                         /* We're only allowed to go -ve in layer on Expanded views */
755                         if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
756                                 this_delta_layer = - i->layer;
757                         }
758                 
759                         /* Set height */
760                         rv->set_height (current_tv->view()->child_height ());
761                 
762                         /* Update show/hidden status as the region view may have come from a hidden track,
763                            or have moved to one.
764                         */
765                         if (current_tv->hidden ()) {
766                                 rv->get_canvas_group()->hide ();
767                         } else {
768                                 rv->get_canvas_group()->show ();
769                         }
770
771                         /* Update the DraggingView */
772                         i->time_axis_view = track_index;
773                         i->layer += this_delta_layer;
774
775                         if (_brushing) {
776                                 _editor->mouse_brush_insert_region (rv, pending_region_position);
777                         } else {
778                                 Duple track_origin;
779
780                                 /* Get the y coordinate of the top of the track that this region is now over */
781                                 track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
782                                 
783                                 /* And adjust for the layer that it should be on */
784                                 StreamView* cv = current_tv->view ();
785                                 switch (cv->layer_display ()) {
786                                 case Overlaid:
787                                         break;
788                                 case Stacked:
789                                         track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
790                                         break;
791                                 case Expanded:
792                                         track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
793                                         break;
794                                 }
795
796                                 /* need to get the parent of the regionview
797                                  * canvas group and get its position in
798                                  * equivalent coordinate space as the trackview
799                                  * we are now dragging over.
800                                  */
801                                 
802                                 /* Now move the region view */
803                                 rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
804                         }
805                 } else {
806
807                         /* Only move the region into the empty dropzone at the bottom if the pointer
808                          * is down there.
809                          */
810
811                         if (current_pointer_y() >= 0) {
812                                 
813                                 Coord last_track_bottom_edge;
814                                 if (!_time_axis_views.empty()) {
815                                         TimeAxisView* last = _time_axis_views.back();
816                                         last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
817                                 } else {
818                                         last_track_bottom_edge = 0;
819                                 }
820
821                                 rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
822                                 i->time_axis_view = -1;
823                         }
824                 }
825                 
826         } /* foreach region */
827
828         _total_x_delta += x_delta;
829
830         if (x_delta != 0 && !_brushing) {
831                 show_verbose_cursor_time (_last_frame_position);
832         }
833
834         _last_pointer_time_axis_view += delta_time_axis_view;
835         _last_pointer_layer += delta_layer;
836 }
837
838 void
839 RegionMoveDrag::motion (GdkEvent* event, bool first_move)
840 {
841         if (_copy && first_move) {
842
843                 if (_x_constrained) {
844                         _editor->begin_reversible_command (Operations::fixed_time_region_copy);
845                 } else {
846                         _editor->begin_reversible_command (Operations::region_copy);
847                 }
848
849                 /* duplicate the regionview(s) and region(s) */
850
851                 list<DraggingView> new_regionviews;
852
853                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
854
855                         RegionView* rv = i->view;
856                         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
857                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(rv);
858
859                         const boost::shared_ptr<const Region> original = rv->region();
860                         boost::shared_ptr<Region> region_copy = RegionFactory::create (original, true);
861                         region_copy->set_position (original->position());
862
863                         RegionView* nrv;
864                         if (arv) {
865                                 boost::shared_ptr<AudioRegion> audioregion_copy
866                                 = boost::dynamic_pointer_cast<AudioRegion>(region_copy);
867
868                                 nrv = new AudioRegionView (*arv, audioregion_copy);
869                         } else if (mrv) {
870                                 boost::shared_ptr<MidiRegion> midiregion_copy
871                                         = boost::dynamic_pointer_cast<MidiRegion>(region_copy);
872                                 nrv = new MidiRegionView (*mrv, midiregion_copy);
873                         } else {
874                                 continue;
875                         }
876
877                         nrv->get_canvas_group()->show ();
878                         new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
879
880                         /* swap _primary to the copy */
881
882                         if (rv == _primary) {
883                                 _primary = nrv;
884                         }
885
886                         /* ..and deselect the one we copied */
887
888                         rv->set_selected (false);
889                 }
890
891                 if (!new_regionviews.empty()) {
892
893                         /* reflect the fact that we are dragging the copies */
894
895                         _views = new_regionviews;
896
897                         swap_grab (new_regionviews.front().view->get_canvas_group (), 0, event ? event->motion.time : 0);
898                 }
899
900         } else if (!_copy && first_move) {
901
902                 if (_x_constrained) {
903                         _editor->begin_reversible_command (_("fixed time region drag"));
904                 } else {
905                         _editor->begin_reversible_command (Operations::region_drag);
906                 }
907         }
908
909         RegionMotionDrag::motion (event, first_move);
910 }
911
912 void
913 RegionMotionDrag::finished (GdkEvent *, bool)
914 {
915         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
916                 if (!(*i)->view()) {
917                         continue;
918                 }
919
920                 if ((*i)->view()->layer_display() == Expanded) {
921                         (*i)->view()->set_layer_display (Stacked);
922                 }
923         }
924 }
925
926 void
927 RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
928 {
929         RegionMotionDrag::finished (ev, movement_occurred);
930         
931         if (!movement_occurred) {
932                 
933                 /* just a click */
934
935                 if (was_double_click() && !_views.empty()) {
936                         DraggingView dv = _views.front();
937                         dv.view->show_region_editor ();
938                         
939                 }
940
941                 return;
942         }
943
944         /* reverse this here so that we have the correct logic to finalize
945            the drag.
946         */
947
948         if (Config->get_edit_mode() == Lock) {
949                 _x_constrained = !_x_constrained;
950         }
951
952         assert (!_views.empty ());
953
954         /* We might have hidden region views so that they weren't visible during the drag
955            (when they have been reparented).  Now everything can be shown again, as region
956            views are back in their track parent groups.
957         */
958         for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
959                 i->view->get_canvas_group()->show ();
960         }
961         
962         bool const changed_position = (_last_frame_position != _primary->region()->position());
963         bool const changed_tracks = (_time_axis_views[_views.front().time_axis_view] != &_views.front().view->get_time_axis_view());
964         framecnt_t const drag_delta = _primary->region()->position() - _last_frame_position;
965         
966         if (_copy) {
967
968                 finished_copy (
969                         changed_position,
970                         changed_tracks,
971                         drag_delta
972                         );
973
974         } else {
975
976                 finished_no_copy (
977                         changed_position,
978                         changed_tracks,
979                         drag_delta
980                         );
981
982         }
983
984         _editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
985 }
986
987 RouteTimeAxisView*
988 RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
989 {                       
990         /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
991            new track.
992          */
993                         
994         try {
995                 if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
996                         list<boost::shared_ptr<AudioTrack> > audio_tracks;
997                         audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
998                         RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
999                         if (rtav) {
1000                                 rtav->set_height (original->current_height());
1001                         }
1002                         return rtav;
1003                 } else {
1004                         ChanCount one_midi_port (DataType::MIDI, 1);
1005                         list<boost::shared_ptr<MidiTrack> > midi_tracks;
1006                         midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
1007                         RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
1008                         if (rtav) {
1009                                 rtav->set_height (original->current_height());
1010                         }
1011                         return rtav;
1012                 }                                               
1013         } catch (...) {
1014                 error << _("Could not create new track after region placed in the drop zone") << endmsg;
1015                 return 0;
1016         }
1017 }
1018
1019 void
1020 RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
1021 {
1022         RegionSelection new_views;
1023         PlaylistSet modified_playlists;
1024         RouteTimeAxisView* new_time_axis_view = 0;      
1025
1026         if (_brushing) {
1027                 /* all changes were made during motion event handlers */
1028
1029                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
1030                         delete i->view;
1031                 }
1032
1033                 _editor->commit_reversible_command ();
1034                 return;
1035         }
1036
1037         /* insert the regions into their new playlists */
1038         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
1039
1040                 RouteTimeAxisView* dest_rtv = 0;
1041
1042                 if (i->view->region()->locked() || i->view->region()->video_locked()) {
1043                         continue;
1044                 }
1045
1046                 framepos_t where;
1047
1048                 if (changed_position && !_x_constrained) {
1049                         where = i->view->region()->position() - drag_delta;
1050                 } else {
1051                         where = i->view->region()->position();
1052                 }
1053                 
1054                 if (i->time_axis_view < 0) {
1055                         if (!new_time_axis_view) {
1056                                 new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
1057                         }
1058                         dest_rtv = new_time_axis_view;
1059                 } else {
1060                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1061                 }               
1062                 
1063                 if (dest_rtv != 0) {
1064                         RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
1065                         if (new_view != 0) {
1066                                 new_views.push_back (new_view);
1067                         }
1068                 }
1069         
1070                 /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
1071                    since deletion will automagically remove it from _views, thus invalidating i as an iterator.
1072                  */
1073
1074                 list<DraggingView>::const_iterator next = i;
1075                 ++next;
1076                 delete i->view;
1077                 i = next;
1078         }
1079
1080         /* If we've created new regions either by copying or moving
1081            to a new track, we want to replace the old selection with the new ones
1082         */
1083
1084         if (new_views.size() > 0) {
1085                 _editor->selection->set (new_views);
1086         }
1087
1088         /* write commands for the accumulated diffs for all our modified playlists */
1089         add_stateful_diff_commands_for_playlists (modified_playlists);
1090
1091         _editor->commit_reversible_command ();
1092 }
1093
1094 void
1095 RegionMoveDrag::finished_no_copy (
1096         bool const changed_position,
1097         bool const changed_tracks,
1098         framecnt_t const drag_delta
1099         )
1100 {
1101         RegionSelection new_views;
1102         PlaylistSet modified_playlists;
1103         PlaylistSet frozen_playlists;
1104         set<RouteTimeAxisView*> views_to_update;
1105         RouteTimeAxisView* new_time_axis_view = 0;
1106
1107         if (_brushing) {
1108                 /* all changes were made during motion event handlers */
1109                 _editor->commit_reversible_command ();
1110                 return;
1111         }
1112
1113         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
1114
1115                 RegionView* rv = i->view;
1116                 RouteTimeAxisView* dest_rtv = 0;
1117
1118                 if (rv->region()->locked() || rv->region()->video_locked()) {
1119                         ++i;
1120                         continue;
1121                 }
1122                 
1123                 if (i->time_axis_view < 0) {
1124                         if (!new_time_axis_view) {
1125                                 new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
1126                         }
1127                         dest_rtv = new_time_axis_view;
1128                 } else {
1129                         dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
1130                 }
1131                         
1132                 assert (dest_rtv);
1133
1134                 double const dest_layer = i->layer;
1135                 
1136                 views_to_update.insert (dest_rtv);
1137
1138                 framepos_t where;
1139
1140                 if (changed_position && !_x_constrained) {
1141                         where = rv->region()->position() - drag_delta;
1142                 } else {
1143                         where = rv->region()->position();
1144                 }
1145
1146                 if (changed_tracks) {
1147
1148                         /* insert into new playlist */
1149
1150                         RegionView* new_view = insert_region_into_playlist (
1151                                 RegionFactory::create (rv->region (), true), dest_rtv, dest_layer, where, modified_playlists
1152                                 );
1153
1154                         if (new_view == 0) {
1155                                 ++i;
1156                                 continue;
1157                         }
1158
1159                         new_views.push_back (new_view);
1160
1161                         /* remove from old playlist */
1162
1163                         /* the region that used to be in the old playlist is not
1164                            moved to the new one - we use a copy of it. as a result,
1165                            any existing editor for the region should no longer be
1166                            visible.
1167                         */
1168                         rv->hide_region_editor();
1169
1170
1171                         remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
1172
1173                 } else {
1174
1175                         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1176
1177                         /* this movement may result in a crossfade being modified, or a layering change,
1178                            so we need to get undo data from the playlist as well as the region.
1179                         */
1180
1181                         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (playlist);
1182                         if (r.second) {
1183                                 playlist->clear_changes ();
1184                         }
1185
1186                         rv->region()->clear_changes ();
1187
1188                         /*
1189                            motion on the same track. plonk the previously reparented region
1190                            back to its original canvas group (its streamview).
1191                            No need to do anything for copies as they are fake regions which will be deleted.
1192                         */
1193
1194                         rv->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1195                         rv->get_canvas_group()->set_y_position (i->initial_y);
1196                         rv->drag_end ();
1197
1198                         /* just change the model */
1199                         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1200                                 playlist->set_layer (rv->region(), dest_layer);
1201                         }
1202
1203                         /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
1204
1205                         r = frozen_playlists.insert (playlist);
1206
1207                         if (r.second) {
1208                                 playlist->freeze ();
1209                         }
1210
1211                         rv->region()->set_position (where);
1212
1213                         _editor->session()->add_command (new StatefulDiffCommand (rv->region()));
1214                 }
1215
1216                 if (changed_tracks) {
1217
1218                         /* OK, this is where it gets tricky. If the playlist was being used by >1 tracks, and the region
1219                            was selected in all of them, then removing it from a playlist will have removed all
1220                            trace of it from _views (i.e. there were N regions selected, we removed 1,
1221                            but since its the same playlist for N tracks, all N tracks updated themselves, removed the
1222                            corresponding regionview, and _views is now empty).
1223
1224                            This could have invalidated any and all iterators into _views.
1225
1226                            The heuristic we use here is: if the region selection is empty, break out of the loop
1227                            here. if the region selection is not empty, then restart the loop because we know that
1228                            we must have removed at least the region(view) we've just been working on as well as any
1229                            that we processed on previous iterations.
1230
1231                            EXCEPT .... if we are doing a copy drag, then _views hasn't been modified and
1232                            we can just iterate.
1233                         */
1234
1235
1236                         if (_views.empty()) {
1237                                 break;
1238                         } else {
1239                                 i = _views.begin();
1240                         }
1241
1242                 } else {
1243                         ++i;
1244                 }
1245         }
1246
1247         /* If we've created new regions either by copying or moving
1248            to a new track, we want to replace the old selection with the new ones
1249         */
1250
1251         if (new_views.size() > 0) {
1252                 _editor->selection->set (new_views);
1253         }
1254
1255         for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
1256                 (*p)->thaw();
1257         }
1258
1259         /* write commands for the accumulated diffs for all our modified playlists */
1260         add_stateful_diff_commands_for_playlists (modified_playlists);
1261
1262         _editor->commit_reversible_command ();
1263
1264         /* We have futzed with the layering of canvas items on our streamviews.
1265            If any region changed layer, this will have resulted in the stream
1266            views being asked to set up their region views, and all will be well.
1267            If not, we might now have badly-ordered region views.  Ask the StreamViews
1268            involved to sort themselves out, just in case.
1269         */
1270
1271         for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
1272                 (*i)->view()->playlist_layered ((*i)->track ());
1273         }
1274 }
1275
1276 /** Remove a region from a playlist, clearing the diff history of the playlist first if necessary.
1277  *  @param region Region to remove.
1278  *  @param playlist playlist To remove from.
1279  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1280  *  that clear_changes () is only called once per playlist.
1281  */
1282 void
1283 RegionMoveDrag::remove_region_from_playlist (
1284         boost::shared_ptr<Region> region,
1285         boost::shared_ptr<Playlist> playlist,
1286         PlaylistSet& modified_playlists
1287         )
1288 {
1289         pair<set<boost::shared_ptr<Playlist> >::iterator, bool> r = modified_playlists.insert (playlist);
1290
1291         if (r.second) {
1292                 playlist->clear_changes ();
1293         }
1294
1295         playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag
1296 }
1297
1298
1299 /** Insert a region into a playlist, handling the recovery of the resulting new RegionView, and
1300  *  clearing the playlist's diff history first if necessary.
1301  *  @param region Region to insert.
1302  *  @param dest_rtv Destination RouteTimeAxisView.
1303  *  @param dest_layer Destination layer.
1304  *  @param where Destination position.
1305  *  @param modified_playlists The playlist will be added to this if it is not there already; used to ensure
1306  *  that clear_changes () is only called once per playlist.
1307  *  @return New RegionView, or 0 if no insert was performed.
1308  */
1309 RegionView *
1310 RegionMoveDrag::insert_region_into_playlist (
1311         boost::shared_ptr<Region> region,
1312         RouteTimeAxisView* dest_rtv,
1313         layer_t dest_layer,
1314         framecnt_t where,
1315         PlaylistSet& modified_playlists
1316         )
1317 {
1318         boost::shared_ptr<Playlist> dest_playlist = dest_rtv->playlist ();
1319         if (!dest_playlist) {
1320                 return 0;
1321         }
1322
1323         /* arrange to collect the new region view that will be created as a result of our playlist insertion */
1324         _new_region_view = 0;
1325         sigc::connection c = dest_rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &RegionMoveDrag::collect_new_region_view));
1326
1327         /* clear history for the playlist we are about to insert to, provided we haven't already done so */
1328         pair<PlaylistSet::iterator, bool> r = modified_playlists.insert (dest_playlist);
1329         if (r.second) {
1330                 dest_playlist->clear_changes ();
1331         }
1332
1333         dest_playlist->add_region (region, where);
1334
1335         if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
1336                 dest_playlist->set_layer (region, dest_layer);
1337         }
1338
1339         c.disconnect ();
1340
1341         assert (_new_region_view);
1342
1343         return _new_region_view;
1344 }
1345
1346 void
1347 RegionMoveDrag::collect_new_region_view (RegionView* rv)
1348 {
1349         _new_region_view = rv;
1350 }
1351
1352 void
1353 RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists)
1354 {
1355         for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1356                 StatefulDiffCommand* c = new StatefulDiffCommand (*i);
1357                 if (!c->empty()) {
1358                         _editor->session()->add_command (c);
1359                 } else {
1360                         delete c;
1361                 }
1362         }
1363 }
1364
1365
1366 void
1367 RegionMoveDrag::aborted (bool movement_occurred)
1368 {
1369         if (_copy) {
1370
1371                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1372                         delete i->view;
1373                 }
1374
1375                 _views.clear ();
1376
1377         } else {
1378                 RegionMotionDrag::aborted (movement_occurred);
1379         }
1380 }
1381
1382 void
1383 RegionMotionDrag::aborted (bool)
1384 {
1385         for (vector<TimeAxisView*>::iterator i = _time_axis_views.begin(); i != _time_axis_views.end(); ++i) {
1386
1387                 StreamView* sview = (*i)->view();
1388
1389                 if (sview) {
1390                         if (sview->layer_display() == Expanded) {
1391                                 sview->set_layer_display (Stacked);
1392                         }
1393                 }
1394         }
1395         
1396         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
1397                 RegionView* rv = i->view;
1398                 TimeAxisView* tv = &(rv->get_time_axis_view ());
1399                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1400                 assert (rtv);
1401                 rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
1402                 rv->get_canvas_group()->set_y_position (0);
1403                 rv->drag_end ();
1404                 rv->move (-_total_x_delta, 0);
1405                 rv->set_height (rtv->view()->child_height ());
1406         }
1407 }
1408
1409 /** @param b true to brush, otherwise false.
1410  *  @param c true to make copies of the regions being moved, otherwise false.
1411  */
1412 RegionMoveDrag::RegionMoveDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool b, bool c)
1413         : RegionMotionDrag (e, i, p, v, b),
1414           _copy (c)
1415 {
1416         DEBUG_TRACE (DEBUG::Drags, "New RegionMoveDrag\n");
1417
1418         double speed = 1;
1419         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&_primary->get_time_axis_view ());
1420         if (rtv && rtv->is_track()) {
1421                 speed = rtv->track()->speed ();
1422         }
1423
1424         _last_frame_position = static_cast<framepos_t> (_primary->region()->position() / speed);
1425 }
1426
1427 void
1428 RegionMoveDrag::setup_pointer_frame_offset ()
1429 {
1430         _pointer_frame_offset = raw_grab_frame() - _last_frame_position;
1431 }
1432
1433 RegionInsertDrag::RegionInsertDrag (Editor* e, boost::shared_ptr<Region> r, RouteTimeAxisView* v, framepos_t pos)
1434         : RegionMotionDrag (e, 0, 0, list<RegionView*> (), false)
1435 {
1436         DEBUG_TRACE (DEBUG::Drags, "New RegionInsertDrag\n");
1437
1438         assert ((boost::dynamic_pointer_cast<AudioRegion> (r) && dynamic_cast<AudioTimeAxisView*> (v)) ||
1439                 (boost::dynamic_pointer_cast<MidiRegion> (r) && dynamic_cast<MidiTimeAxisView*> (v)));
1440
1441         _primary = v->view()->create_region_view (r, false, false);
1442
1443         _primary->get_canvas_group()->show ();
1444         _primary->set_position (pos, 0);
1445         _views.push_back (DraggingView (_primary, this, v));
1446
1447         _last_frame_position = pos;
1448
1449         _item = _primary->get_canvas_group ();
1450 }
1451
1452 void
1453 RegionInsertDrag::finished (GdkEvent *, bool)
1454 {
1455         RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[_views.front().time_axis_view]);
1456
1457         _primary->get_canvas_group()->reparent (dest_rtv->view()->canvas_item());
1458         _primary->get_canvas_group()->set_y_position (0);
1459
1460         boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
1461
1462         _editor->begin_reversible_command (Operations::insert_region);
1463         playlist->clear_changes ();
1464         playlist->add_region (_primary->region (), _last_frame_position);
1465
1466         // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably
1467         if (Config->get_edit_mode() == Ripple) {
1468                 playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region());
1469         }
1470
1471         _editor->session()->add_command (new StatefulDiffCommand (playlist));
1472         _editor->commit_reversible_command ();
1473
1474         delete _primary;
1475         _primary = 0;
1476         _views.clear ();
1477 }
1478
1479 void
1480 RegionInsertDrag::aborted (bool)
1481 {
1482         delete _primary;
1483         _primary = 0;
1484         _views.clear ();
1485 }
1486
1487 RegionSpliceDrag::RegionSpliceDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1488         : RegionMoveDrag (e, i, p, v, false, false)
1489 {
1490         DEBUG_TRACE (DEBUG::Drags, "New RegionSpliceDrag\n");
1491 }
1492
1493 struct RegionSelectionByPosition {
1494     bool operator() (RegionView*a, RegionView* b) {
1495             return a->region()->position () < b->region()->position();
1496     }
1497 };
1498
1499 void
1500 RegionSpliceDrag::motion (GdkEvent* event, bool)
1501 {
1502         /* Which trackview is this ? */
1503
1504         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1505         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1506
1507         /* The region motion is only processed if the pointer is over
1508            an audio track.
1509         */
1510
1511         if (!tv || !tv->is_track()) {
1512                 /* To make sure we hide the verbose canvas cursor when the mouse is
1513                    not held over an audio track.
1514                 */
1515                 _editor->verbose_cursor()->hide ();
1516                 return;
1517         } else {
1518                 _editor->verbose_cursor()->show ();
1519         }
1520
1521         int dir;
1522
1523         if ((_drags->current_pointer_x() - last_pointer_x()) > 0) {
1524                 dir = 1;
1525         } else {
1526                 dir = -1;
1527         }
1528
1529         RegionSelection copy;
1530         _editor->selection->regions.by_position(copy);
1531
1532         framepos_t const pf = adjusted_current_frame (event);
1533
1534         for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1535
1536                 RouteTimeAxisView* atv = dynamic_cast<RouteTimeAxisView*> (&(*i)->get_time_axis_view());
1537
1538                 if (!atv) {
1539                         continue;
1540                 }
1541
1542                 boost::shared_ptr<Playlist> playlist;
1543
1544                 if ((playlist = atv->playlist()) == 0) {
1545                         continue;
1546                 }
1547
1548                 if (!playlist->region_is_shuffle_constrained ((*i)->region())) {
1549                         continue;
1550                 }
1551
1552                 if (dir > 0) {
1553                         if (pf < (*i)->region()->last_frame() + 1) {
1554                                 continue;
1555                         }
1556                 } else {
1557                         if (pf > (*i)->region()->first_frame()) {
1558                                 continue;
1559                         }
1560                 }
1561
1562
1563                 playlist->shuffle ((*i)->region(), dir);
1564         }
1565 }
1566
1567 void
1568 RegionSpliceDrag::finished (GdkEvent* event, bool movement_occurred)
1569 {
1570         RegionMoveDrag::finished (event, movement_occurred);
1571 }
1572
1573 void
1574 RegionSpliceDrag::aborted (bool)
1575 {
1576         /* XXX: TODO */
1577 }
1578
1579 /***
1580  * ripple mode...
1581  */
1582
1583 void
1584 RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress)
1585 {
1586
1587         boost::shared_ptr<RegionList> rl = tav->playlist()->regions_with_start_within (Evoral::Range<framepos_t>(where, max_framepos));
1588
1589         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tav);
1590         RegionSelection to_ripple;
1591         for (RegionList::iterator i = rl->begin(); i != rl->end(); ++i) {
1592                 if ((*i)->position() >= where) {
1593                         to_ripple.push_back (rtv->view()->find_view(*i));
1594                 }
1595         }
1596
1597         for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) {
1598                 if (!exclude.contains (*i)) {
1599                         // the selection has already been added to _views
1600
1601                         if (drag_in_progress) {
1602                                 // do the same things that RegionMotionDrag::motion does when
1603                                 // first_move is true, for the region views that we're adding
1604                                 // to _views this time
1605
1606                                 (*i)->drag_start();
1607                                 ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
1608                                 Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
1609                                 Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
1610                                 rvg->reparent (_editor->_drag_motion_group);
1611
1612                                 // we only need to move in the y direction
1613                                 Duple fudge = rv_canvas_offset - dmg_canvas_offset;
1614                                 fudge.x = 0;
1615                                 rvg->move (fudge);
1616
1617                         }
1618                         _views.push_back (DraggingView (*i, this, tav));
1619                 }
1620         }
1621 }
1622
1623 void
1624 RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
1625 {
1626
1627         for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
1628                 // we added all the regions after the selection 
1629
1630                 std::list<DraggingView>::iterator to_erase = i++;
1631                 if (!_editor->selection->regions.contains (to_erase->view)) {
1632                         // restore the non-selected regions to their original playlist & positions,
1633                         // and then ripple them back by the length of the regions that were dragged away
1634                         // do the same things as RegionMotionDrag::aborted
1635
1636                         RegionView *rv = to_erase->view;
1637                         TimeAxisView* tv = &(rv->get_time_axis_view ());
1638                         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
1639                         assert (rtv);
1640
1641                         // plonk them back onto their own track
1642                         rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
1643                         rv->get_canvas_group()->set_y_position (0);
1644                         rv->drag_end ();
1645
1646                         if (move_regions) {
1647                                 // move the underlying region to match the view
1648                                 rv->region()->set_position (rv->region()->position() + amount);
1649                         } else {
1650                                 // restore the view to match the underlying region's original position
1651                                 rv->move(-amount, 0);   // second parameter is y delta - seems 0 is OK
1652                         }
1653
1654                         rv->set_height (rtv->view()->child_height ());
1655                         _views.erase (to_erase);
1656                 }
1657         }
1658 }
1659
1660 bool
1661 RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
1662 {
1663         if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
1664                 if (delta_track) {
1665                         return allow_moves_across_tracks;
1666                 } else {
1667                         return true;
1668                 }
1669         }
1670         return false;
1671 }
1672
1673 RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v)
1674         : RegionMoveDrag (e, i, p, v, false, false)
1675 {
1676         DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n");
1677         // compute length of selection
1678         RegionSelection selected_regions = _editor->selection->regions;
1679         selection_length = selected_regions.end_frame() - selected_regions.start();
1680
1681         // we'll only allow dragging to another track in ripple mode if all the regions
1682         // being dragged start off on the same track
1683         allow_moves_across_tracks = (selected_regions.playlists().size() == 1);
1684         prev_tav = NULL;
1685         prev_amount = 0;
1686         exclude = new RegionList;
1687         for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) {
1688                 exclude->push_back((*i)->region());
1689         }
1690
1691         // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple
1692         RegionSelection copy;
1693         selected_regions.by_position(copy); // get selected regions sorted by position into copy
1694
1695         std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
1696         std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1697
1698         for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1699                 // find ripple start point on each applicable playlist
1700                 RegionView *first_selected_on_this_track = NULL;
1701                 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1702                         if ((*i)->region()->playlist() == (*pi)) {
1703                                 // region is on this playlist - it's the first, because they're sorted
1704                                 first_selected_on_this_track = *i;
1705                                 break;
1706                         }
1707                 }
1708                 assert (first_selected_on_this_track); // we should always find the region in one of the playlists...
1709                 add_all_after_to_views (
1710                                 &first_selected_on_this_track->get_time_axis_view(),
1711                                 first_selected_on_this_track->region()->position(),
1712                                 selected_regions, false);
1713         }
1714
1715         if (allow_moves_across_tracks) {
1716                 orig_tav = &(*selected_regions.begin())->get_time_axis_view();
1717         } else {
1718                 orig_tav = NULL;
1719         }
1720
1721 }
1722
1723 void
1724 RegionRippleDrag::motion (GdkEvent* event, bool first_move)
1725 {
1726         /* Which trackview is this ? */
1727
1728         pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
1729         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1730
1731         /* The region motion is only processed if the pointer is over
1732            an audio track.
1733          */
1734
1735         if (!tv || !tv->is_track()) {
1736                 /* To make sure we hide the verbose canvas cursor when the mouse is
1737                    not held over an audiotrack.
1738                  */
1739                 _editor->verbose_cursor()->hide ();
1740                 return;
1741         }
1742
1743         framepos_t where = adjusted_current_frame (event);
1744         assert (where >= 0);
1745         framepos_t after;
1746         double delta = compute_x_delta (event, &after);
1747
1748         framecnt_t amount = _editor->pixel_to_sample (delta);
1749
1750         if (allow_moves_across_tracks) {
1751                 // all the originally selected regions were on the same track
1752
1753                 framecnt_t adjust = 0;
1754                 if (prev_tav && tv != prev_tav) {
1755                         // dragged onto a different track 
1756                         // remove the unselected regions from _views, restore them to their original positions
1757                         // and add the regions after the drop point on the new playlist to _views instead.
1758                         // undo the effect of rippling the previous playlist, and include the effect of removing
1759                         // the dragged region(s) from this track
1760
1761                         remove_unselected_from_views (prev_amount, false);
1762                         // ripple previous playlist according to the regions that have been removed onto the new playlist
1763                         prev_tav->playlist()->ripple(prev_position, -selection_length, exclude);
1764                         prev_amount = 0;
1765
1766                         // move just the selected regions
1767                         RegionMoveDrag::motion(event, first_move); 
1768
1769                         // ensure that the ripple operation on the new playlist inserts selection_length time 
1770                         adjust = selection_length;
1771                         // ripple the new current playlist
1772                         tv->playlist()->ripple (where, amount+adjust, exclude);
1773
1774                         // add regions after point where drag entered this track to subsequent ripples
1775                         add_all_after_to_views (tv, where, _editor->selection->regions, true);
1776
1777                 } else {
1778                         // motion on same track
1779                         RegionMoveDrag::motion(event, first_move); 
1780                 }
1781                 prev_tav = tv;
1782
1783                 // remember what we've done to this playlist so we can undo it if the selection is dragged to another track
1784                 prev_position = where;
1785         } else {
1786                 // selection encompasses multiple tracks - just drag
1787                 // cross-track drags are forbidden
1788                 RegionMoveDrag::motion(event, first_move); 
1789         }
1790
1791         if (!_x_constrained) {
1792                 prev_amount += amount;
1793         }
1794
1795         _last_frame_position = after;
1796 }
1797
1798 void
1799 RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
1800 {
1801         if (!movement_occurred) {
1802                 
1803                 /* just a click */
1804
1805                 if (was_double_click() && !_views.empty()) {
1806                         DraggingView dv = _views.front();
1807                         dv.view->show_region_editor ();
1808                         
1809                 }
1810
1811                 return;
1812         }
1813
1814         _editor->begin_reversible_command(_("Ripple drag"));
1815         
1816         // remove the regions being rippled from the dragging view, updating them to 
1817         // their new positions
1818         remove_unselected_from_views (prev_amount, true);
1819
1820         if (allow_moves_across_tracks) {
1821                 if (orig_tav) {
1822                         // if regions were dragged across tracks, we've rippled any later
1823                         // regions on the track the regions were dragged off, so we need
1824                         // to add the original track to the undo record
1825                         orig_tav->playlist()->clear_changes();
1826                         vector<Command*> cmds;
1827                         orig_tav->playlist()->rdiff (cmds);
1828                         _editor->session()->add_commands (cmds);
1829                 }
1830                 if (prev_tav && prev_tav != orig_tav) {
1831                         prev_tav->playlist()->clear_changes();
1832                         vector<Command*> cmds;
1833                         prev_tav->playlist()->rdiff (cmds);
1834                         _editor->session()->add_commands (cmds);
1835                 }
1836         } else {
1837                 // selection spanned multiple tracks - all will need adding to undo record
1838
1839                 std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
1840                 std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
1841
1842                 for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
1843                         (*pi)->clear_changes();
1844                         vector<Command*> cmds;
1845                         (*pi)->rdiff (cmds);
1846                         _editor->session()->add_commands (cmds);
1847                 }
1848         }
1849
1850         // other modified playlists are added to undo by RegionMoveDrag::finished()
1851         RegionMoveDrag::finished (event, movement_occurred);
1852         _editor->commit_reversible_command();
1853 }
1854
1855 void
1856 RegionRippleDrag::aborted (bool movement_occurred)
1857 {
1858         RegionMoveDrag::aborted (movement_occurred);
1859         _views.clear ();
1860 }
1861
1862
1863 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
1864         : Drag (e, i),
1865           _view (dynamic_cast<MidiTimeAxisView*> (v))
1866 {
1867         DEBUG_TRACE (DEBUG::Drags, "New RegionCreateDrag\n");
1868
1869         assert (_view);
1870 }
1871
1872 void
1873 RegionCreateDrag::motion (GdkEvent* event, bool first_move)
1874 {
1875         if (first_move) {
1876                 _region = add_midi_region (_view);
1877                 _view->playlist()->freeze ();
1878         } else {
1879                 if (_region) {
1880                         framepos_t const f = adjusted_current_frame (event);
1881                         if (f < grab_frame()) {
1882                                 _region->set_position (f);
1883                         }
1884
1885                         /* Don't use a zero-length region, and subtract 1 frame from the snapped length
1886                            so that if this region is duplicated, its duplicate starts on
1887                            a snap point rather than 1 frame after a snap point.  Otherwise things get
1888                            a bit confusing as if a region starts 1 frame after a snap point, one cannot
1889                            place snapped notes at the start of the region.
1890                         */
1891
1892                         framecnt_t const len = (framecnt_t) fabs ((double)(f - grab_frame () - 1));
1893                         _region->set_length (len < 1 ? 1 : len);
1894                 }
1895         }
1896 }
1897
1898 void
1899 RegionCreateDrag::finished (GdkEvent*, bool movement_occurred)
1900 {
1901         if (!movement_occurred) {
1902                 add_midi_region (_view);
1903         } else {
1904                 _view->playlist()->thaw ();
1905         }
1906 }
1907
1908 void
1909 RegionCreateDrag::aborted (bool)
1910 {
1911         if (_region) {
1912                 _view->playlist()->thaw ();
1913         }
1914
1915         /* XXX */
1916 }
1917
1918 NoteResizeDrag::NoteResizeDrag (Editor* e, ArdourCanvas::Item* i)
1919         : Drag (e, i)
1920         , region (0)
1921 {
1922         DEBUG_TRACE (DEBUG::Drags, "New NoteResizeDrag\n");
1923 }
1924
1925 void
1926 NoteResizeDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*ignored*/)
1927 {
1928         Gdk::Cursor* cursor;
1929         NoteBase* cnote = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1930         assert (cnote);
1931         float x_fraction = cnote->mouse_x_fraction ();
1932
1933         if (x_fraction > 0.0 && x_fraction < 0.25) {
1934                 cursor = _editor->cursors()->left_side_trim;
1935                 at_front = true;
1936         } else  {
1937                 cursor = _editor->cursors()->right_side_trim;
1938                 at_front = false;
1939         }
1940
1941         Drag::start_grab (event, cursor);
1942
1943         region = &cnote->region_view();
1944
1945         _item->grab ();
1946
1947         if (event->motion.state & Keyboard::PrimaryModifier) {
1948                 relative = false;
1949         } else {
1950                 relative = true;
1951         }
1952
1953         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1954
1955         if (ms.size() > 1) {
1956                 /* has to be relative, may make no sense otherwise */
1957                 relative = true;
1958         }
1959
1960         /* select this note; if it is already selected, preserve the existing selection,
1961            otherwise make this note the only one selected.
1962         */
1963         region->note_selected (cnote, cnote->selected ());
1964
1965         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ) {
1966                 MidiRegionSelection::iterator next;
1967                 next = r;
1968                 ++next;
1969                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1970                 if (mrv) {
1971                         mrv->begin_resizing (at_front);
1972                 }
1973                 r = next;
1974         }
1975 }
1976
1977 void
1978 NoteResizeDrag::motion (GdkEvent* /*event*/, bool /*first_move*/)
1979 {
1980         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1981         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1982                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1983                 assert (nb);
1984                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1985                 if (mrv) {
1986                         mrv->update_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
1987                 }
1988         }
1989 }
1990
1991 void
1992 NoteResizeDrag::finished (GdkEvent*, bool /*movement_occurred*/)
1993 {
1994         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
1995         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
1996                 NoteBase* nb = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
1997                 assert (nb);
1998                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
1999                 if (mrv) {
2000                         mrv->commit_resizing (nb, at_front, _drags->current_pointer_x() - grab_x(), relative);
2001                 }
2002         }
2003 }
2004
2005 void
2006 NoteResizeDrag::aborted (bool)
2007 {
2008         MidiRegionSelection& ms (_editor->get_selection().midi_regions);
2009         for (MidiRegionSelection::iterator r = ms.begin(); r != ms.end(); ++r) {
2010                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*r);
2011                 if (mrv) {
2012                         mrv->abort_resizing ();
2013                 }
2014         }
2015 }
2016
2017 AVDraggingView::AVDraggingView (RegionView* v)
2018         : view (v)
2019 {
2020         initial_position = v->region()->position ();
2021 }
2022
2023 VideoTimeLineDrag::VideoTimeLineDrag (Editor* e, ArdourCanvas::Item* i)
2024         : Drag (e, i)
2025 {
2026         DEBUG_TRACE (DEBUG::Drags, "New VideoTimeLineDrag\n");
2027
2028         RegionSelection rs;
2029         TrackViewList empty;
2030         empty.clear();
2031         _editor->get_regions_after(rs, (framepos_t) 0, empty);
2032         std::list<RegionView*> views = rs.by_layer();
2033
2034         for (list<RegionView*>::iterator i = views.begin(); i != views.end(); ++i) {
2035                 RegionView* rv = (*i);
2036                 if (!rv->region()->video_locked()) {
2037                         continue;
2038                 }
2039                 _views.push_back (AVDraggingView (rv));
2040         }
2041 }
2042
2043 void
2044 VideoTimeLineDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2045 {
2046         Drag::start_grab (event);
2047         if (_editor->session() == 0) {
2048                 return;
2049         }
2050
2051         _startdrag_video_offset=ARDOUR_UI::instance()->video_timeline->get_offset();
2052         _max_backwards_drag = (
2053                           ARDOUR_UI::instance()->video_timeline->get_duration()
2054                         + ARDOUR_UI::instance()->video_timeline->get_offset()
2055                         - ceil(ARDOUR_UI::instance()->video_timeline->get_apv())
2056                         );
2057
2058         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2059                 if (i->initial_position < _max_backwards_drag || _max_backwards_drag < 0) {
2060                         _max_backwards_drag = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv (i->initial_position);
2061                 }
2062         }
2063         DEBUG_TRACE (DEBUG::Drags, string_compose("VideoTimeLineDrag: max backwards-drag: %1\n", _max_backwards_drag));
2064
2065         char buf[128];
2066         Timecode::Time timecode;
2067         _editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
2068         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);
2069         show_verbose_cursor_text (buf);
2070 }
2071
2072 void
2073 VideoTimeLineDrag::motion (GdkEvent* event, bool first_move)
2074 {
2075         if (_editor->session() == 0) {
2076                 return;
2077         }
2078         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2079                 return;
2080         }
2081
2082         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame() + _pointer_frame_offset;
2083         dt = ARDOUR_UI::instance()->video_timeline->quantify_frames_to_apv(_startdrag_video_offset+dt) - _startdrag_video_offset;
2084
2085         if (_max_backwards_drag >= 0 && dt <= - _max_backwards_drag) {
2086                 dt = - _max_backwards_drag;
2087         }
2088
2089         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset+dt);
2090         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2091
2092         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2093                 RegionView* rv = i->view;
2094                 DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
2095                 if (first_move) {
2096                         rv->drag_start ();
2097                         rv->region()->clear_changes ();
2098                         rv->region()->suspend_property_changes();
2099                 }
2100                 rv->region()->set_position(i->initial_position + dt);
2101                 rv->region_changed(ARDOUR::Properties::position);
2102         }
2103
2104         const framepos_t offset = ARDOUR_UI::instance()->video_timeline->get_offset();
2105         Timecode::Time timecode;
2106         Timecode::Time timediff;
2107         char buf[128];
2108         _editor->session()->sample_to_timecode(abs(offset), timecode, true /* use_offset */, false /* use_subframes */ );
2109         _editor->session()->sample_to_timecode(abs(dt), timediff, false /* use_offset */, false /* use_subframes */ );
2110         snprintf (buf, sizeof (buf),
2111                         "%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2112                         "\n%s\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32
2113                         , _("Video Start:"),
2114                                 (offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames
2115                         , _("Diff:"),
2116                                 (dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
2117                                 );
2118         show_verbose_cursor_text (buf);
2119 }
2120
2121 void
2122 VideoTimeLineDrag::finished (GdkEvent * /*event*/, bool movement_occurred)
2123 {
2124         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2125                 return;
2126         }
2127
2128         if (!movement_occurred || ! _editor->session()) {
2129                 return;
2130         }
2131
2132         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2133
2134         _editor->begin_reversible_command (_("Move Video"));
2135
2136         XMLNode &before = ARDOUR_UI::instance()->video_timeline->get_state();
2137         ARDOUR_UI::instance()->video_timeline->save_undo();
2138         XMLNode &after = ARDOUR_UI::instance()->video_timeline->get_state();
2139         _editor->session()->add_command(new MementoCommand<VideoTimeLine>(*(ARDOUR_UI::instance()->video_timeline), &before, &after));
2140
2141         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2142                 i->view->drag_end();
2143                 i->view->region()->resume_property_changes ();
2144
2145                 _editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
2146         }
2147
2148         _editor->session()->maybe_update_session_range(
2149                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
2150                         std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
2151                         );
2152
2153
2154         _editor->commit_reversible_command ();
2155 }
2156
2157 void
2158 VideoTimeLineDrag::aborted (bool)
2159 {
2160         if (ARDOUR_UI::instance()->video_timeline->is_offset_locked()) {
2161                 return;
2162         }
2163         ARDOUR_UI::instance()->video_timeline->set_offset(_startdrag_video_offset);
2164         ARDOUR_UI::instance()->flush_videotimeline_cache(true);
2165
2166         for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2167                 i->view->region()->resume_property_changes ();
2168                 i->view->region()->set_position(i->initial_position);
2169         }
2170 }
2171
2172 TrimDrag::TrimDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> const & v, bool preserve_fade_anchor)
2173         : RegionDrag (e, i, p, v)
2174         , _preserve_fade_anchor (preserve_fade_anchor)
2175         , _jump_position_when_done (false)
2176 {
2177         DEBUG_TRACE (DEBUG::Drags, "New TrimDrag\n");
2178 }
2179
2180 void
2181 TrimDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
2182 {
2183         double speed = 1.0;
2184         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2185         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2186
2187         if (tv && tv->is_track()) {
2188                 speed = tv->track()->speed();
2189         }
2190
2191         framepos_t const region_start = (framepos_t) (_primary->region()->position() / speed);
2192         framepos_t const region_end = (framepos_t) (_primary->region()->last_frame() / speed);
2193         framecnt_t const region_length = (framecnt_t) (_primary->region()->length() / speed);
2194
2195         framepos_t const pf = adjusted_current_frame (event);
2196
2197         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
2198                 /* Move the contents of the region around without changing the region bounds */
2199                 _operation = ContentsTrim;
2200                 Drag::start_grab (event, _editor->cursors()->trimmer);
2201         } else {
2202                 /* These will get overridden for a point trim.*/
2203                 if (pf < (region_start + region_length/2)) {
2204                         /* closer to front */
2205                         _operation = StartTrim;
2206
2207                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2208                                 Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
2209                         } else {
2210                                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
2211                         }
2212                 } else {
2213                         /* closer to end */
2214                         _operation = EndTrim;
2215                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2216                                 Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
2217                         } else {
2218                                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
2219                         }
2220                 }
2221         }
2222
2223         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2224                 _jump_position_when_done = true;
2225         }
2226
2227         switch (_operation) {
2228         case StartTrim:
2229                 show_verbose_cursor_time (region_start);
2230                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2231                         i->view->trim_front_starting ();
2232                 }
2233                 break;
2234         case EndTrim:
2235                 show_verbose_cursor_time (region_end);
2236                 break;
2237         case ContentsTrim:
2238                 show_verbose_cursor_time (pf);
2239                 break;
2240         }
2241
2242         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2243                 i->view->region()->suspend_property_changes ();
2244         }
2245 }
2246
2247 void
2248 TrimDrag::motion (GdkEvent* event, bool first_move)
2249 {
2250         RegionView* rv = _primary;
2251
2252         double speed = 1.0;
2253         TimeAxisView* tvp = &_primary->get_time_axis_view ();
2254         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2255         pair<set<boost::shared_ptr<Playlist> >::iterator,bool> insert_result;
2256         frameoffset_t frame_delta = 0;
2257
2258         if (tv && tv->is_track()) {
2259                 speed = tv->track()->speed();
2260         }
2261
2262         framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
2263
2264         if (first_move) {
2265
2266                 string trim_type;
2267
2268                 switch (_operation) {
2269                 case StartTrim:
2270                         trim_type = "Region start trim";
2271                         break;
2272                 case EndTrim:
2273                         trim_type = "Region end trim";
2274                         break;
2275                 case ContentsTrim:
2276                         trim_type = "Region content trim";
2277                         break;
2278                 default:
2279                         assert(0);
2280                         break;
2281                 }
2282
2283                 _editor->begin_reversible_command (trim_type);
2284
2285                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2286                         RegionView* rv = i->view;
2287                         rv->enable_display (false);
2288                         rv->region()->playlist()->clear_owned_changes ();
2289
2290                         AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (rv);
2291
2292                         if (arv) {
2293                                 arv->temporarily_hide_envelope ();
2294                                 arv->drag_start ();
2295                         }
2296
2297                         boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2298                         insert_result = _editor->motion_frozen_playlists.insert (pl);
2299
2300                         if (insert_result.second) {
2301                                 pl->freeze();
2302                         }
2303                 }
2304         }
2305
2306         bool non_overlap_trim = false;
2307
2308         if (event && Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
2309                 non_overlap_trim = true;
2310         }
2311
2312         /* contstrain trim to fade length */
2313         if (_preserve_fade_anchor) {
2314                 switch (_operation) {
2315                         case StartTrim:
2316                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2317                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2318                                         if (!arv) continue;
2319                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2320                                         if (ar->locked()) continue;
2321                                         framecnt_t len = ar->fade_in()->back()->when;
2322                                         if (len < dt) dt = min(dt, len);
2323                                 }
2324                                 break;
2325                         case EndTrim:
2326                                 for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2327                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2328                                         if (!arv) continue;
2329                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2330                                         if (ar->locked()) continue;
2331                                         framecnt_t len = ar->fade_out()->back()->when;
2332                                         if (len < -dt) dt = max(dt, -len);
2333                                 }
2334                                 break;
2335                         case ContentsTrim:
2336                                 break;
2337                 }
2338         }
2339
2340
2341         switch (_operation) {
2342         case StartTrim:
2343                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2344                         bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
2345                         if (changed && _preserve_fade_anchor) {
2346                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2347                                 if (arv) {
2348                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2349                                         framecnt_t len = ar->fade_in()->back()->when;
2350                                         framecnt_t diff = ar->first_frame() - i->initial_position;
2351                                         framepos_t new_length = len - diff;
2352                                         i->anchored_fade_length = min (ar->length(), new_length);
2353                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true  /*START*/ );
2354                                         arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
2355                                 }
2356                         }
2357                 }
2358                 break;
2359
2360         case EndTrim:
2361                 for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
2362                         bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
2363                         if (changed && _preserve_fade_anchor) {
2364                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2365                                 if (arv) {
2366                                         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2367                                         framecnt_t len = ar->fade_out()->back()->when;
2368                                         framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
2369                                         framepos_t new_length = len + diff;
2370                                         i->anchored_fade_length = min (ar->length(), new_length);
2371                                         //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false  /*END*/ );
2372                                         arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
2373                                 }
2374                         }
2375                 }
2376                 break;
2377
2378         case ContentsTrim:
2379                 {
2380                         frame_delta = (last_pointer_frame() - adjusted_current_frame(event));
2381
2382                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2383                                 i->view->move_contents (frame_delta);
2384                         }
2385                 }
2386                 break;
2387         }
2388
2389         switch (_operation) {
2390         case StartTrim:
2391                 show_verbose_cursor_time ((framepos_t) (rv->region()->position() / speed));
2392                 break;
2393         case EndTrim:
2394                 show_verbose_cursor_time ((framepos_t) (rv->region()->last_frame() / speed));
2395                 break;
2396         case ContentsTrim:
2397                 // show_verbose_cursor_time (frame_delta);
2398                 break;
2399         }
2400 }
2401
2402
2403 void
2404 TrimDrag::finished (GdkEvent* event, bool movement_occurred)
2405 {
2406         if (movement_occurred) {
2407                 motion (event, false);
2408
2409                 if (_operation == StartTrim) {
2410                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2411                                 {
2412                                         /* This must happen before the region's StatefulDiffCommand is created, as it may
2413                                            `correct' (ahem) the region's _start from being negative to being zero.  It
2414                                            needs to be zero in the undo record.
2415                                         */
2416                                         i->view->trim_front_ending ();
2417                                 }
2418                                 if (_preserve_fade_anchor) {
2419                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2420                                         if (arv) {
2421                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2422                                                 arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
2423                                                 ar->set_fade_in_length(i->anchored_fade_length);
2424                                                 ar->set_fade_in_active(true);
2425                                         }
2426                                 }
2427                                 if (_jump_position_when_done) {
2428                                         i->view->region()->set_position (i->initial_position);
2429                                 }
2430                         }
2431                 } else if (_operation == EndTrim) {
2432                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2433                                 if (_preserve_fade_anchor) {
2434                                         AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
2435                                         if (arv) {
2436                                                 boost::shared_ptr<AudioRegion> ar (arv->audio_region());
2437                                                 arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
2438                                                 ar->set_fade_out_length(i->anchored_fade_length);
2439                                                 ar->set_fade_out_active(true);
2440                                         }
2441                                 }
2442                                 if (_jump_position_when_done) {
2443                                         i->view->region()->set_position (i->initial_end - i->view->region()->length());
2444                                 }
2445                         }
2446                 }
2447
2448                 if (!_views.empty()) {
2449                         if (_operation == StartTrim) {
2450                                 _editor->maybe_locate_with_edit_preroll(
2451                                         _views.begin()->view->region()->position());
2452                         }
2453                         if (_operation == EndTrim) {
2454                                 _editor->maybe_locate_with_edit_preroll(
2455                                         _views.begin()->view->region()->position() +
2456                                         _views.begin()->view->region()->length());
2457                         }
2458                 }
2459         
2460                 if (!_editor->selection->selected (_primary)) {
2461                         _primary->thaw_after_trim ();
2462                 } else {
2463
2464                         set<boost::shared_ptr<Playlist> > diffed_playlists;
2465
2466                         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2467                                 i->view->thaw_after_trim ();
2468                                 i->view->enable_display (true);
2469
2470                                 /* Trimming one region may affect others on the playlist, so we need
2471                                    to get undo Commands from the whole playlist rather than just the
2472                                    region.  Use diffed_playlists to make sure we don't diff a given
2473                                    playlist more than once.
2474                                 */
2475                                 boost::shared_ptr<Playlist> p = i->view->region()->playlist ();
2476                                 if (diffed_playlists.find (p) == diffed_playlists.end()) {
2477                                         vector<Command*> cmds;
2478                                         p->rdiff (cmds);
2479                                         _editor->session()->add_commands (cmds);
2480                                         diffed_playlists.insert (p);
2481                                 }
2482                         }
2483                 }
2484
2485                 for (set<boost::shared_ptr<Playlist> >::iterator p = _editor->motion_frozen_playlists.begin(); p != _editor->motion_frozen_playlists.end(); ++p) {
2486                         (*p)->thaw ();
2487                 }
2488
2489                 _editor->motion_frozen_playlists.clear ();
2490                 _editor->commit_reversible_command();
2491
2492         } else {
2493                 /* no mouse movement */
2494                 _editor->point_trim (event, adjusted_current_frame (event));
2495         }
2496
2497         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2498                 if (_operation == StartTrim) {
2499                         i->view->trim_front_ending ();
2500                 }
2501
2502                 i->view->region()->resume_property_changes ();
2503         }
2504 }
2505
2506 void
2507 TrimDrag::aborted (bool movement_occurred)
2508 {
2509         /* Our motion method is changing model state, so use the Undo system
2510            to cancel.  Perhaps not ideal, as this will leave an Undo point
2511            behind which may be slightly odd from the user's point of view.
2512         */
2513
2514         finished (0, true);
2515
2516         if (movement_occurred) {
2517                 _editor->undo ();
2518         }
2519
2520         for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
2521                 i->view->region()->resume_property_changes ();
2522         }
2523 }
2524
2525 void
2526 TrimDrag::setup_pointer_frame_offset ()
2527 {
2528         list<DraggingView>::iterator i = _views.begin ();
2529         while (i != _views.end() && i->view != _primary) {
2530                 ++i;
2531         }
2532
2533         if (i == _views.end()) {
2534                 return;
2535         }
2536
2537         switch (_operation) {
2538         case StartTrim:
2539                 _pointer_frame_offset = raw_grab_frame() - i->initial_position;
2540                 break;
2541         case EndTrim:
2542                 _pointer_frame_offset = raw_grab_frame() - i->initial_end;
2543                 break;
2544         case ContentsTrim:
2545                 break;
2546         }
2547 }
2548
2549 MeterMarkerDrag::MeterMarkerDrag (Editor* e, ArdourCanvas::Item* i, bool c)
2550         : Drag (e, i),
2551           _copy (c)
2552 {
2553         DEBUG_TRACE (DEBUG::Drags, "New MeterMarkerDrag\n");
2554         _marker = reinterpret_cast<MeterMarker*> (_item->get_data ("marker"));
2555         assert (_marker);
2556 }
2557
2558 void
2559 MeterMarkerDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
2560 {
2561         Drag::start_grab (event, cursor);
2562         show_verbose_cursor_time (adjusted_current_frame(event));
2563 }
2564
2565 void
2566 MeterMarkerDrag::setup_pointer_frame_offset ()
2567 {
2568         _pointer_frame_offset = raw_grab_frame() - _marker->meter().frame();
2569 }
2570
2571 void
2572 MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
2573 {
2574         if (!_marker->meter().movable()) {
2575                 return;
2576         }
2577
2578         if (first_move) {
2579
2580                 // create a dummy marker for visual representation of moving the
2581                 // section, because whether its a copy or not, we're going to 
2582                 // leave or lose the original marker (leave if its a copy; lose if its
2583                 // not, because we'll remove it from the map).
2584                 
2585                 MeterSection section (_marker->meter());
2586
2587                 if (!section.movable()) {
2588                         return;
2589                 }
2590                 
2591                 char name[64];
2592                 snprintf (name, sizeof(name), "%g/%g", _marker->meter().divisions_per_bar(), _marker->meter().note_divisor ());
2593                 
2594                 _marker = new MeterMarker (
2595                         *_editor,
2596                         *_editor->meter_group,
2597                         ARDOUR_UI::config()->color ("meter marker"),
2598                         name,
2599                         *new MeterSection (_marker->meter())
2600                 );
2601                 
2602                 /* use the new marker for the grab */
2603                 swap_grab (&_marker->the_item(), 0, GDK_CURRENT_TIME);
2604
2605                 if (!_copy) {
2606                         TempoMap& map (_editor->session()->tempo_map());
2607                         /* get current state */
2608                         before_state = &map.get_state();
2609                         /* remove the section while we drag it */
2610                         map.remove_meter (section, true);
2611                 }
2612         }
2613
2614         framepos_t const pf = adjusted_current_frame (event);
2615
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()->color ("tempo marker"),
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->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::MouseDraw) {
3650                 /* always active in mouse draw */
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->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         , _original_pointer_time_axis (-1)
4116         , _time_selection_at_start (!_editor->get_selection().time.empty())
4117 {
4118         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
4119         
4120         if (_time_selection_at_start) {
4121                 start_at_start = _editor->get_selection().time.start();
4122                 end_at_start = _editor->get_selection().time.end_frame();
4123         }
4124 }
4125
4126 void
4127 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
4128 {
4129         if (_editor->session() == 0) {
4130                 return;
4131         }
4132
4133         Gdk::Cursor* cursor = 0;
4134
4135         switch (_operation) {
4136         case CreateSelection:
4137                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4138                         _add = true;
4139                 } else {
4140                         _add = false;
4141                 }
4142                 cursor = _editor->cursors()->selector;
4143                 Drag::start_grab (event, cursor);
4144                 break;
4145
4146         case SelectionStartTrim:
4147                 if (_editor->clicked_axisview) {
4148                         _editor->clicked_axisview->order_selection_trims (_item, true);
4149                 }
4150                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
4151                 break;
4152
4153         case SelectionEndTrim:
4154                 if (_editor->clicked_axisview) {
4155                         _editor->clicked_axisview->order_selection_trims (_item, false);
4156                 }
4157                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
4158                 break;
4159
4160         case SelectionMove:
4161                 Drag::start_grab (event, cursor);
4162                 break;
4163
4164         case SelectionExtend:
4165                 Drag::start_grab (event, cursor);
4166                 break;
4167         }
4168
4169         if (_operation == SelectionMove) {
4170                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
4171         } else {
4172                 show_verbose_cursor_time (adjusted_current_frame (event));
4173         }
4174
4175         _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
4176 }
4177
4178 void
4179 SelectionDrag::setup_pointer_frame_offset ()
4180 {
4181         switch (_operation) {
4182         case CreateSelection:
4183                 _pointer_frame_offset = 0;
4184                 break;
4185
4186         case SelectionStartTrim:
4187         case SelectionMove:
4188                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
4189                 break;
4190
4191         case SelectionEndTrim:
4192                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
4193                 break;
4194
4195         case SelectionExtend:
4196                 break;
4197         }
4198 }
4199
4200 void
4201 SelectionDrag::motion (GdkEvent* event, bool first_move)
4202 {
4203         framepos_t start = 0;
4204         framepos_t end = 0;
4205         framecnt_t length = 0;
4206         framecnt_t distance = 0;
4207
4208         framepos_t const pending_position = adjusted_current_frame (event);
4209
4210         if (_operation != CreateSelection && pending_position == last_pointer_frame()) {
4211                 return;
4212         }
4213
4214         switch (_operation) {
4215         case CreateSelection:
4216         {
4217                 framepos_t grab = grab_frame ();
4218
4219                 if (first_move) {
4220                         grab = adjusted_current_frame (event, false);
4221                         if (grab < pending_position) {
4222                                 _editor->snap_to (grab, RoundDownMaybe);
4223                         }  else {
4224                                 _editor->snap_to (grab, RoundUpMaybe);
4225                         }
4226                 }
4227
4228                 if (pending_position < grab) {
4229                         start = pending_position;
4230                         end = grab;
4231                 } else {
4232                         end = pending_position;
4233                         start = grab;
4234                 }
4235
4236                 /* first drag: Either add to the selection
4237                    or create a new selection
4238                 */
4239
4240                 if (first_move) {
4241
4242                         if (_add) {
4243
4244                                 /* adding to the selection */
4245                                 _editor->set_selected_track_as_side_effect (Selection::Add);
4246                                 _editor->clicked_selection = _editor->selection->add (start, end);
4247                                 _add = false;
4248                                 
4249                         } else {
4250
4251                                 /* new selection */
4252
4253                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4254                                         _editor->set_selected_track_as_side_effect (Selection::Set);
4255                                 }
4256
4257                                 _editor->clicked_selection = _editor->selection->set (start, end);
4258                         }
4259                 }
4260                 
4261                 //if user is selecting a range on an automation track, bail out here before we get to the grouped stuff, 
4262                 // because the grouped stuff will start working on tracks (routeTAVs), and end up removing this 
4263                 AutomationTimeAxisView *atest = dynamic_cast<AutomationTimeAxisView *>(_editor->clicked_axisview);
4264                 if (atest) {
4265                         _editor->selection->add (atest);
4266                         break; 
4267                 }
4268                 
4269                 /* select all tracks within the rectangle that we've marked out so far */
4270                 TrackViewList new_selection;
4271                 TrackViewList& all_tracks (_editor->track_views);
4272
4273                 ArdourCanvas::Coord const top = grab_y();
4274                 ArdourCanvas::Coord const bottom = current_pointer_y();
4275
4276                 if (top >= 0 && bottom >= 0) {
4277
4278                         //first, find the tracks that are covered in the y range selection
4279                         for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
4280                                 if ((*i)->covered_by_y_range (top, bottom)) {
4281                                         new_selection.push_back (*i);
4282                                 }
4283                         }
4284
4285                         //now find any tracks that are GROUPED with the tracks we selected
4286                         TrackViewList grouped_add = new_selection;
4287                         for (TrackViewList::const_iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
4288                                 RouteTimeAxisView *n = dynamic_cast<RouteTimeAxisView *>(*i);
4289                                 if ( n && n->route()->route_group() && n->route()->route_group()->is_active() && n->route()->route_group()->enabled_property (ARDOUR::Properties::select.property_id) ) {
4290                                         for (TrackViewList::const_iterator j = all_tracks.begin(); j != all_tracks.end(); ++j) {
4291                                                 RouteTimeAxisView *check = dynamic_cast<RouteTimeAxisView *>(*j);
4292                                                 if ( check && (n != check) && (check->route()->route_group() == n->route()->route_group()) )
4293                                                         grouped_add.push_back (*j);
4294                                         }
4295                                 }
4296                         }
4297
4298                         //now compare our list with the current selection, and add or remove as necessary
4299                         //( NOTE: most mouse moves don't change the selection so we can't just SET it for every mouse move; it gets clunky )
4300                         TrackViewList tracks_to_add;
4301                         TrackViewList tracks_to_remove;
4302                         for (TrackViewList::const_iterator i = grouped_add.begin(); i != grouped_add.end(); ++i)
4303                                 if ( !_editor->selection->tracks.contains ( *i ) )
4304                                         tracks_to_add.push_back ( *i );
4305                         for (TrackViewList::const_iterator i = _editor->selection->tracks.begin(); i != _editor->selection->tracks.end(); ++i)
4306                                 if ( !grouped_add.contains ( *i ) )
4307                                         tracks_to_remove.push_back ( *i );
4308                         _editor->selection->add(tracks_to_add);
4309                         _editor->selection->remove(tracks_to_remove);
4310
4311                 }
4312         }
4313         break;
4314
4315         case SelectionStartTrim:
4316
4317                 start = _editor->selection->time[_editor->clicked_selection].start;
4318                 end = _editor->selection->time[_editor->clicked_selection].end;
4319
4320                 if (pending_position > end) {
4321                         start = end;
4322                 } else {
4323                         start = pending_position;
4324                 }
4325                 break;
4326
4327         case SelectionEndTrim:
4328
4329                 start = _editor->selection->time[_editor->clicked_selection].start;
4330                 end = _editor->selection->time[_editor->clicked_selection].end;
4331
4332                 if (pending_position < start) {
4333                         end = start;
4334                 } else {
4335                         end = pending_position;
4336                 }
4337
4338                 break;
4339                 
4340         case SelectionMove:
4341
4342                 start = _editor->selection->time[_editor->clicked_selection].start;
4343                 end = _editor->selection->time[_editor->clicked_selection].end;
4344
4345                 length = end - start;
4346                 distance = pending_position - start;
4347                 start = pending_position;
4348                 _editor->snap_to (start);
4349
4350                 end = start + length;
4351
4352                 break;
4353
4354         case SelectionExtend:
4355                 break;
4356         }
4357
4358         if (start != end) {
4359                 switch (_operation) {
4360                 case SelectionMove:     
4361                         if (_time_selection_at_start) {
4362                                 _editor->selection->move_time (distance);
4363                         }
4364                         break;
4365                 default:
4366                         _editor->selection->replace (_editor->clicked_selection, start, end);
4367                 }
4368         }
4369
4370         if (_operation == SelectionMove) {
4371                 show_verbose_cursor_time(start);
4372         } else {
4373                 show_verbose_cursor_time(pending_position);
4374         }
4375 }
4376
4377 void
4378 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
4379 {
4380         Session* s = _editor->session();
4381
4382         if (movement_occurred) {
4383                 motion (event, false);
4384                 /* XXX this is not object-oriented programming at all. ick */
4385                 if (_editor->selection->time.consolidate()) {
4386                         _editor->selection->TimeChanged ();
4387                 }
4388
4389                 /* XXX what if its a music time selection? */
4390                 if (s) {
4391                         if ( s->get_play_range() && s->transport_rolling() ) {
4392                                 s->request_play_range (&_editor->selection->time, true);
4393                         } else {
4394                                 if (Config->get_follow_edits() && !s->transport_rolling()) {
4395                                         if (_operation == SelectionEndTrim)
4396                                                 _editor->maybe_locate_with_edit_preroll( _editor->get_selection().time.end_frame());
4397                                         else
4398                                                 s->request_locate (_editor->get_selection().time.start());
4399                                 }
4400                         }
4401                 }
4402
4403         } else {
4404                 /* just a click, no pointer movement.
4405                  */
4406
4407                 if (_operation == SelectionExtend) {
4408                         if (_time_selection_at_start) {
4409                                 framepos_t pos = adjusted_current_frame (event, false);
4410                                 framepos_t start = min (pos, start_at_start);
4411                                 framepos_t end = max (pos, end_at_start);
4412                                 _editor->selection->set (start, end);
4413                         }
4414                 } else {
4415                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
4416                                 if (_editor->clicked_selection) {
4417                                         _editor->selection->remove (_editor->clicked_selection);
4418                                 }
4419                         } else {
4420                                 if (!_editor->clicked_selection) {
4421                                         _editor->selection->clear_time();
4422                                 }
4423                         }
4424                 }
4425
4426                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
4427                         _editor->selection->set (_editor->clicked_axisview);
4428                 }
4429                         
4430                 if (s && s->get_play_range () && s->transport_rolling()) {
4431                         s->request_stop (false, false);
4432                 }
4433
4434         }
4435
4436         _editor->stop_canvas_autoscroll ();
4437         _editor->clicked_selection = 0;
4438 }
4439
4440 void
4441 SelectionDrag::aborted (bool)
4442 {
4443         /* XXX: TODO */
4444 }
4445
4446 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4447         : Drag (e, i, false),
4448           _operation (o),
4449           _copy (false)
4450 {
4451         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4452
4453         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group, 
4454                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
4455                                                                       physical_screen_height (_editor->get_window())));
4456         _drag_rect->hide ();
4457
4458         _drag_rect->set_fill_color (ARDOUR_UI::config()->color ("range drag rect"));
4459         _drag_rect->set_outline_color (ARDOUR_UI::config()->color ("range drag rect"));
4460 }
4461
4462 void
4463 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4464 {
4465         if (_editor->session() == 0) {
4466                 return;
4467         }
4468
4469         Gdk::Cursor* cursor = 0;
4470
4471         if (!_editor->temp_location) {
4472                 _editor->temp_location = new Location (*_editor->session());
4473         }
4474
4475         switch (_operation) {
4476         case CreateSkipMarker:
4477         case CreateRangeMarker:
4478         case CreateTransportMarker:
4479         case CreateCDMarker:
4480
4481                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4482                         _copy = true;
4483                 } else {
4484                         _copy = false;
4485                 }
4486                 cursor = _editor->cursors()->selector;
4487                 break;
4488         }
4489
4490         Drag::start_grab (event, cursor);
4491
4492         show_verbose_cursor_time (adjusted_current_frame (event));
4493 }
4494
4495 void
4496 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4497 {
4498         framepos_t start = 0;
4499         framepos_t end = 0;
4500         ArdourCanvas::Rectangle *crect;
4501
4502         switch (_operation) {
4503         case CreateSkipMarker:
4504                 crect = _editor->range_bar_drag_rect;
4505                 break;
4506         case CreateRangeMarker:
4507                 crect = _editor->range_bar_drag_rect;
4508                 break;
4509         case CreateTransportMarker:
4510                 crect = _editor->transport_bar_drag_rect;
4511                 break;
4512         case CreateCDMarker:
4513                 crect = _editor->cd_marker_bar_drag_rect;
4514                 break;
4515         default:
4516                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4517                 return;
4518                 break;
4519         }
4520
4521         framepos_t const pf = adjusted_current_frame (event);
4522
4523         if (_operation == CreateSkipMarker || _operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4524                 framepos_t grab = grab_frame ();
4525                 _editor->snap_to (grab);
4526
4527                 if (pf < grab_frame()) {
4528                         start = pf;
4529                         end = grab;
4530                 } else {
4531                         end = pf;
4532                         start = grab;
4533                 }
4534
4535                 /* first drag: Either add to the selection
4536                    or create a new selection.
4537                 */
4538
4539                 if (first_move) {
4540
4541                         _editor->temp_location->set (start, end);
4542
4543                         crect->show ();
4544
4545                         update_item (_editor->temp_location);
4546                         _drag_rect->show();
4547                         //_drag_rect->raise_to_top();
4548
4549                 }
4550         }
4551
4552         if (start != end) {
4553                 _editor->temp_location->set (start, end);
4554
4555                 double x1 = _editor->sample_to_pixel (start);
4556                 double x2 = _editor->sample_to_pixel (end);
4557                 crect->set_x0 (x1);
4558                 crect->set_x1 (x2);
4559
4560                 update_item (_editor->temp_location);
4561         }
4562
4563         show_verbose_cursor_time (pf);
4564
4565 }
4566
4567 void
4568 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4569 {
4570         Location * newloc = 0;
4571         string rangename;
4572         int flags;
4573
4574         if (movement_occurred) {
4575                 motion (event, false);
4576                 _drag_rect->hide();
4577
4578                 switch (_operation) {
4579                 case CreateSkipMarker:
4580                 case CreateRangeMarker:
4581                 case CreateCDMarker:
4582                     {
4583                         XMLNode &before = _editor->session()->locations()->get_state();
4584                         if (_operation == CreateSkipMarker) {
4585                                 _editor->begin_reversible_command (_("new skip marker"));
4586                                 _editor->session()->locations()->next_available_name(rangename,_("skip"));
4587                                 flags = Location::IsRangeMarker | Location::IsSkip;
4588                                 _editor->range_bar_drag_rect->hide();
4589                         } else if (_operation == CreateCDMarker) {
4590                                 _editor->session()->locations()->next_available_name(rangename, _("CD"));
4591                                 _editor->begin_reversible_command (_("new CD marker"));
4592                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
4593                                 _editor->cd_marker_bar_drag_rect->hide();
4594                         } else {
4595                                 _editor->begin_reversible_command (_("new skip marker"));
4596                                 _editor->session()->locations()->next_available_name(rangename, _("unnamed"));
4597                                 flags = Location::IsRangeMarker;
4598                                 _editor->range_bar_drag_rect->hide();
4599                         }
4600                         newloc = new Location (
4601                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4602                                 );
4603
4604                         _editor->session()->locations()->add (newloc, true);
4605                         XMLNode &after = _editor->session()->locations()->get_state();
4606                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4607                         _editor->commit_reversible_command ();
4608                         break;
4609                     }
4610
4611                 case CreateTransportMarker:
4612                         // popup menu to pick loop or punch
4613                         _editor->new_transport_marker_context_menu (&event->button, _item);
4614                         break;
4615                 }
4616
4617         } else {
4618
4619                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4620
4621                 if (_operation == CreateTransportMarker) {
4622
4623                         /* didn't drag, so just locate */
4624
4625                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4626
4627                 } else if (_operation == CreateCDMarker) {
4628
4629                         /* didn't drag, but mark is already created so do
4630                          * nothing */
4631
4632                 } else { /* operation == CreateRangeMarker || CreateSkipMarker */
4633                         
4634
4635                         framepos_t start;
4636                         framepos_t end;
4637
4638                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4639
4640                         if (end == max_framepos) {
4641                                 end = _editor->session()->current_end_frame ();
4642                         }
4643
4644                         if (start == max_framepos) {
4645                                 start = _editor->session()->current_start_frame ();
4646                         }
4647
4648                         switch (_editor->mouse_mode) {
4649                         case MouseObject:
4650                                 /* find the two markers on either side and then make the selection from it */
4651                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4652                                 break;
4653
4654                         case MouseRange:
4655                                 /* find the two markers on either side of the click and make the range out of it */
4656                                 _editor->selection->set (start, end);
4657                                 break;
4658
4659                         default:
4660                                 break;
4661                         }
4662                 }
4663         }
4664
4665         _editor->stop_canvas_autoscroll ();
4666 }
4667
4668 void
4669 RangeMarkerBarDrag::aborted (bool)
4670 {
4671         /* XXX: TODO */
4672 }
4673
4674 void
4675 RangeMarkerBarDrag::update_item (Location* location)
4676 {
4677         double const x1 = _editor->sample_to_pixel (location->start());
4678         double const x2 = _editor->sample_to_pixel (location->end());
4679
4680         _drag_rect->set_x0 (x1);
4681         _drag_rect->set_x1 (x2);
4682 }
4683
4684 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4685         : Drag (e, i)
4686         , _cumulative_dx (0)
4687         , _cumulative_dy (0)
4688 {
4689         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4690
4691         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4692         assert (_primary);
4693         _region = &_primary->region_view ();
4694         _note_height = _region->midi_stream_view()->note_height ();
4695 }
4696
4697 void
4698 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4699 {
4700         Drag::start_grab (event);
4701
4702         if (!(_was_selected = _primary->selected())) {
4703
4704                 /* tertiary-click means extend selection - we'll do that on button release,
4705                    so don't add it here, because otherwise we make it hard to figure
4706                    out the "extend-to" range.
4707                 */
4708
4709                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4710
4711                 if (!extend) {
4712                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4713
4714                         if (add) {
4715                                 _region->note_selected (_primary, true);
4716                         } else {
4717                                 _region->unique_select (_primary);
4718                         }
4719                 }
4720         }
4721 }
4722
4723 /** @return Current total drag x change in frames */
4724 frameoffset_t
4725 NoteDrag::total_dx () const
4726 {
4727         /* dx in frames */
4728         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4729
4730         /* primary note time */
4731         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4732
4733         /* new time of the primary note in session frames */
4734         frameoffset_t st = n + dx;
4735
4736         framepos_t const rp = _region->region()->position ();
4737
4738         /* prevent the note being dragged earlier than the region's position */
4739         st = max (st, rp);
4740
4741         /* snap and return corresponding delta */
4742         return _region->snap_frame_to_frame (st - rp) + rp - n;
4743 }
4744
4745 /** @return Current total drag y change in note number */
4746 int8_t
4747 NoteDrag::total_dy () const
4748 {
4749         MidiStreamView* msv = _region->midi_stream_view ();
4750         double const y = _region->midi_view()->y_position ();
4751         /* new current note */
4752         uint8_t n = msv->y_to_note (current_pointer_y () - y);
4753         /* clamp */
4754         n = max (msv->lowest_note(), n);
4755         n = min (msv->highest_note(), n);
4756         /* and work out delta */
4757         return n - msv->y_to_note (grab_y() - y);
4758 }
4759
4760 void
4761 NoteDrag::motion (GdkEvent *, bool)
4762 {
4763         /* Total change in x and y since the start of the drag */
4764         frameoffset_t const dx = total_dx ();
4765         int8_t const dy = total_dy ();
4766
4767         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4768         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4769         double const tdy = -dy * _note_height - _cumulative_dy;
4770
4771         if (tdx || tdy) {
4772                 _cumulative_dx += tdx;
4773                 _cumulative_dy += tdy;
4774
4775                 int8_t note_delta = total_dy();
4776
4777                 _region->move_selection (tdx, tdy, note_delta);
4778
4779                 /* the new note value may be the same as the old one, but we
4780                  * don't know what that means because the selection may have
4781                  * involved more than one note and we might be doing something
4782                  * odd with them. so show the note value anyway, always.
4783                  */
4784
4785                 char buf[12];
4786                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4787                 
4788                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4789                           (int) floor ((double)new_note));
4790
4791                 show_verbose_cursor_text (buf);
4792         }
4793 }
4794
4795 void
4796 NoteDrag::finished (GdkEvent* ev, bool moved)
4797 {
4798         if (!moved) {
4799                 /* no motion - select note */
4800                 
4801                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4802                     _editor->current_mouse_mode() == Editing::MouseDraw) {
4803                         
4804                         if (_was_selected) {
4805                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4806                                 if (add) {
4807                                         _region->note_deselected (_primary);
4808                                 }
4809                         } else {
4810                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4811                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4812
4813                                 if (!extend && !add && _region->selection_size() > 1) {
4814                                         _region->unique_select (_primary);
4815                                 } else if (extend) {
4816                                         _region->note_selected (_primary, true, true);
4817                                 } else {
4818                                         /* it was added during button press */
4819                                 }
4820                         }
4821                 }
4822         } else {
4823                 _region->note_dropped (_primary, total_dx(), total_dy());
4824         }
4825 }
4826
4827 void
4828 NoteDrag::aborted (bool)
4829 {
4830         /* XXX: TODO */
4831 }
4832
4833 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4834 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4835         : Drag (editor, atv->base_item ())
4836         , _ranges (r)
4837         , _nothing_to_drag (false)
4838 {
4839         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4840         y_origin = atv->y_position();
4841         setup (atv->lines ());
4842 }
4843
4844 /** Make an AutomationRangeDrag for region gain lines */
4845 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4846         : Drag (editor, rv->get_canvas_group ())
4847         , _ranges (r)
4848         , _nothing_to_drag (false)
4849 {
4850         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4851
4852         list<boost::shared_ptr<AutomationLine> > lines;
4853         lines.push_back (rv->get_gain_line ());
4854         y_origin = rv->get_time_axis_view().y_position();
4855         setup (lines);
4856 }
4857
4858 /** @param lines AutomationLines to drag.
4859  *  @param offset Offset from the session start to the points in the AutomationLines.
4860  */
4861 void
4862 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4863 {
4864         /* find the lines that overlap the ranges being dragged */
4865         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4866         while (i != lines.end ()) {
4867                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4868                 ++j;
4869
4870                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4871
4872                 /* check this range against all the AudioRanges that we are using */
4873                 list<AudioRange>::const_iterator k = _ranges.begin ();
4874                 while (k != _ranges.end()) {
4875                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4876                                 break;
4877                         }
4878                         ++k;
4879                 }
4880
4881                 /* add it to our list if it overlaps at all */
4882                 if (k != _ranges.end()) {
4883                         Line n;
4884                         n.line = *i;
4885                         n.state = 0;
4886                         n.range = r;
4887                         _lines.push_back (n);
4888                 }
4889
4890                 i = j;
4891         }
4892
4893         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4894 }
4895
4896 double
4897 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4898 {
4899         return 1.0 - ((global_y - y_origin) / line->height());
4900 }
4901
4902 void
4903 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4904 {
4905         Drag::start_grab (event, cursor);
4906
4907         /* Get line states before we start changing things */
4908         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4909                 i->state = &i->line->get_state ();
4910                 i->original_fraction = y_fraction (i->line, current_pointer_y());
4911         }
4912
4913         if (_ranges.empty()) {
4914
4915                 /* No selected time ranges: drag all points */
4916                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4917                         uint32_t const N = i->line->npoints ();
4918                         for (uint32_t j = 0; j < N; ++j) {
4919                                 i->points.push_back (i->line->nth (j));
4920                         }
4921                 }
4922
4923         } else {
4924
4925                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4926
4927                         framecnt_t const half = (i->start + i->end) / 2;
4928
4929                         /* find the line that this audio range starts in */
4930                         list<Line>::iterator j = _lines.begin();
4931                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4932                                 ++j;
4933                         }
4934
4935                         if (j != _lines.end()) {
4936                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4937
4938                                 /* j is the line that this audio range starts in; fade into it;
4939                                    64 samples length plucked out of thin air.
4940                                 */
4941
4942                                 framepos_t a = i->start + 64;
4943                                 if (a > half) {
4944                                         a = half;
4945                                 }
4946
4947                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4948                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4949
4950                                 the_list->editor_add (p, the_list->eval (p));
4951                                 the_list->editor_add (q, the_list->eval (q));
4952                         }
4953
4954                         /* same thing for the end */
4955
4956                         j = _lines.begin();
4957                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4958                                 ++j;
4959                         }
4960
4961                         if (j != _lines.end()) {
4962                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4963
4964                                 /* j is the line that this audio range starts in; fade out of it;
4965                                    64 samples length plucked out of thin air.
4966                                 */
4967
4968                                 framepos_t b = i->end - 64;
4969                                 if (b < half) {
4970                                         b = half;
4971                                 }
4972
4973                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4974                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4975
4976                                 the_list->editor_add (p, the_list->eval (p));
4977                                 the_list->editor_add (q, the_list->eval (q));
4978                         }
4979                 }
4980
4981                 _nothing_to_drag = true;
4982
4983                 /* Find all the points that should be dragged and put them in the relevant
4984                    points lists in the Line structs.
4985                 */
4986
4987                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4988
4989                         uint32_t const N = i->line->npoints ();
4990                         for (uint32_t j = 0; j < N; ++j) {
4991
4992                                 /* here's a control point on this line */
4993                                 ControlPoint* p = i->line->nth (j);
4994                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4995
4996                                 /* see if it's inside a range */
4997                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4998                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4999                                         ++k;
5000                                 }
5001
5002                                 if (k != _ranges.end()) {
5003                                         /* dragging this point */
5004                                         _nothing_to_drag = false;
5005                                         i->points.push_back (p);
5006                                 }
5007                         }
5008                 }
5009         }
5010
5011         if (_nothing_to_drag) {
5012                 return;
5013         }
5014
5015         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5016                 i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
5017         }
5018 }
5019
5020 void
5021 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
5022 {
5023         if (_nothing_to_drag) {
5024                 return;
5025         }
5026
5027         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
5028                 float const f = y_fraction (l->line, current_pointer_y());
5029                 /* we are ignoring x position for this drag, so we can just pass in anything */
5030                 uint32_t ignored;
5031                 l->line->drag_motion (0, f, true, false, ignored);
5032                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
5033         }
5034 }
5035
5036 void
5037 AutomationRangeDrag::finished (GdkEvent* event, bool)
5038 {
5039         if (_nothing_to_drag) {
5040                 return;
5041         }
5042
5043         motion (event, false);
5044         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5045                 i->line->end_drag (false, 0);
5046         }
5047
5048         _editor->commit_reversible_command ();
5049 }
5050
5051 void
5052 AutomationRangeDrag::aborted (bool)
5053 {
5054         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
5055                 i->line->reset ();
5056         }
5057 }
5058
5059 DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
5060         : view (v)
5061         , initial_time_axis_view (itav)
5062 {
5063         /* note that time_axis_view may be null if the regionview was created
5064          * as part of a copy operation.
5065          */
5066         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
5067         layer = v->region()->layer ();
5068         initial_y = v->get_canvas_group()->position().y;
5069         initial_playlist = v->region()->playlist ();
5070         initial_position = v->region()->position ();
5071         initial_end = v->region()->position () + v->region()->length ();
5072 }
5073
5074 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
5075         : Drag (e, i->canvas_item ())
5076         , _region_view (r)
5077         , _patch_change (i)
5078         , _cumulative_dx (0)
5079 {
5080         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
5081                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
5082                                                    grab_frame()));
5083 }
5084
5085 void
5086 PatchChangeDrag::motion (GdkEvent* ev, bool)
5087 {
5088         framepos_t f = adjusted_current_frame (ev);
5089         boost::shared_ptr<Region> r = _region_view->region ();
5090         f = max (f, r->position ());
5091         f = min (f, r->last_frame ());
5092
5093         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
5094         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
5095         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
5096         _cumulative_dx = dxu;
5097 }
5098
5099 void
5100 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
5101 {
5102         if (!movement_occurred) {
5103                 return;
5104         }
5105
5106         boost::shared_ptr<Region> r (_region_view->region ());
5107         framepos_t f = adjusted_current_frame (ev);
5108         f = max (f, r->position ());
5109         f = min (f, r->last_frame ());
5110
5111         _region_view->move_patch_change (
5112                 *_patch_change,
5113                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
5114                 );
5115 }
5116
5117 void
5118 PatchChangeDrag::aborted (bool)
5119 {
5120         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
5121 }
5122
5123 void
5124 PatchChangeDrag::setup_pointer_frame_offset ()
5125 {
5126         boost::shared_ptr<Region> region = _region_view->region ();
5127         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
5128 }
5129
5130 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
5131         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5132         , _region_view (rv)
5133 {
5134
5135 }
5136
5137 void
5138 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
5139 {
5140         _region_view->update_drag_selection (
5141                 x1, x2, y1, y2,
5142                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier));
5143 }
5144
5145 void
5146 MidiRubberbandSelectDrag::deselect_things ()
5147 {
5148         /* XXX */
5149 }
5150
5151 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
5152         : RubberbandSelectDrag (e, rv->get_canvas_group ())
5153         , _region_view (rv)
5154 {
5155         _vertical_only = true;
5156 }
5157
5158 void
5159 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
5160 {
5161         double const y = _region_view->midi_view()->y_position ();
5162
5163         y1 = max (0.0, y1 - y);
5164         y2 = max (0.0, y2 - y);
5165         
5166         _region_view->update_vertical_drag_selection (
5167                 y1,
5168                 y2,
5169                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
5170                 );
5171 }
5172
5173 void
5174 MidiVerticalSelectDrag::deselect_things ()
5175 {
5176         /* XXX */
5177 }
5178
5179 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
5180         : RubberbandSelectDrag (e, i)
5181 {
5182
5183 }
5184
5185 void
5186 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
5187 {
5188         if (drag_in_progress) {
5189                 /* We just want to select things at the end of the drag, not during it */
5190                 return;
5191         }
5192         
5193         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
5194
5195         _editor->begin_reversible_command (_("rubberband selection"));
5196
5197         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
5198
5199         _editor->commit_reversible_command ();
5200 }
5201
5202 void
5203 EditorRubberbandSelectDrag::deselect_things ()
5204 {
5205         if (!getenv("ARDOUR_SAE")) {
5206                 _editor->selection->clear_tracks();
5207         }
5208         _editor->selection->clear_regions();
5209         _editor->selection->clear_points ();
5210         _editor->selection->clear_lines ();
5211 }
5212
5213 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
5214         : Drag (e, i)
5215         , _region_view (rv)
5216         , _drag_rect (0)
5217 {
5218         _note[0] = _note[1] = 0;
5219 }
5220
5221 NoteCreateDrag::~NoteCreateDrag ()
5222 {
5223         delete _drag_rect;
5224 }
5225
5226 framecnt_t
5227 NoteCreateDrag::grid_frames (framepos_t t) const
5228 {
5229         bool success;
5230         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
5231         if (!success) {
5232                 grid_beats = Evoral::MusicalTime(1);
5233         }
5234
5235         return _region_view->region_beats_to_region_frames (grid_beats);
5236 }
5237
5238 void
5239 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
5240 {
5241         Drag::start_grab (event, cursor);
5242                                                  
5243         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
5244
5245         framepos_t pf = _drags->current_pointer_frame ();
5246         framecnt_t const g = grid_frames (pf);
5247
5248         /* Hack so that we always snap to the note that we are over, instead of snapping
5249            to the next one if we're more than halfway through the one we're over.
5250         */
5251         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
5252                 pf -= g / 2;
5253         }
5254
5255         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
5256         _note[1] = _note[0];
5257
5258         MidiStreamView* sv = _region_view->midi_stream_view ();
5259         double const x = _editor->sample_to_pixel (_note[0]);
5260         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
5261
5262         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
5263         _drag_rect->set_outline_all ();
5264         _drag_rect->set_outline_color (0xffffff99);
5265         _drag_rect->set_fill_color (0xffffff66);
5266 }
5267
5268 void
5269 NoteCreateDrag::motion (GdkEvent* event, bool)
5270 {
5271         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
5272         double const x0 = _editor->sample_to_pixel (_note[0]);
5273         double const x1 = _editor->sample_to_pixel (_note[1]);
5274         _drag_rect->set_x0 (std::min(x0, x1));
5275         _drag_rect->set_x1 (std::max(x0, x1));
5276 }
5277
5278 void
5279 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
5280 {
5281         if (!had_movement) {
5282                 return;
5283         }
5284         
5285         framepos_t const start = min (_note[0], _note[1]);
5286         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
5287
5288         framecnt_t const g = grid_frames (start);
5289         Evoral::MusicalTime const one_tick = Evoral::MusicalTime::ticks(1);
5290         
5291         if (_editor->snap_mode() == SnapNormal && length < g) {
5292                 length = g;
5293         }
5294
5295         Evoral::MusicalTime length_beats = max (
5296                 one_tick, _region_view->region_frames_to_region_beats (length) - one_tick);
5297
5298         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
5299 }
5300
5301 double
5302 NoteCreateDrag::y_to_region (double y) const
5303 {
5304         double x = 0;
5305         _region_view->get_canvas_group()->canvas_to_item (x, y);
5306         return y;
5307 }
5308
5309 void
5310 NoteCreateDrag::aborted (bool)
5311 {
5312         
5313 }
5314
5315 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
5316         : Drag (e, i)
5317         , arv (rv)
5318         , start (start_yn)
5319 {
5320         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
5321 }
5322
5323 void
5324 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
5325 {
5326         Drag::start_grab (event, cursor);
5327 }
5328
5329 void
5330 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
5331 {
5332         double distance;
5333         double new_length;
5334         framecnt_t len;
5335
5336         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5337
5338         if (start) {
5339                 distance = _drags->current_pointer_x() - grab_x();
5340                 len = ar->fade_in()->back()->when;
5341         } else {
5342                 distance = grab_x() - _drags->current_pointer_x();
5343                 len = ar->fade_out()->back()->when;
5344         }
5345
5346         /* how long should it be ? */
5347
5348         new_length = len + _editor->pixel_to_sample (distance);
5349
5350         /* now check with the region that this is legal */
5351
5352         new_length = ar->verify_xfade_bounds (new_length, start);
5353
5354         if (start) {
5355                 arv->reset_fade_in_shape_width (ar, new_length);
5356         } else {
5357                 arv->reset_fade_out_shape_width (ar, new_length);
5358         }
5359 }
5360
5361 void
5362 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5363 {
5364         double distance;
5365         double new_length;
5366         framecnt_t len;
5367
5368         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5369
5370         if (start) {
5371                 distance = _drags->current_pointer_x() - grab_x();
5372                 len = ar->fade_in()->back()->when;
5373         } else {
5374                 distance = grab_x() - _drags->current_pointer_x();
5375                 len = ar->fade_out()->back()->when;
5376         }
5377
5378         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5379         
5380         _editor->begin_reversible_command ("xfade trim");
5381         ar->playlist()->clear_owned_changes (); 
5382
5383         if (start) {
5384                 ar->set_fade_in_length (new_length);
5385         } else {
5386                 ar->set_fade_out_length (new_length);
5387         }
5388
5389         /* Adjusting the xfade may affect other regions in the playlist, so we need
5390            to get undo Commands from the whole playlist rather than just the
5391            region.
5392         */
5393
5394         vector<Command*> cmds;
5395         ar->playlist()->rdiff (cmds);
5396         _editor->session()->add_commands (cmds);
5397         _editor->commit_reversible_command ();
5398
5399 }
5400
5401 void
5402 CrossfadeEdgeDrag::aborted (bool)
5403 {
5404         if (start) {
5405                 arv->redraw_start_xfade ();
5406         } else {
5407                 arv->redraw_end_xfade ();
5408         }
5409 }
5410
5411 RegionCutDrag::RegionCutDrag (Editor* e, ArdourCanvas::Item* item, framepos_t pos)
5412         : Drag (e, item, true)
5413         , line (new EditorCursor (*e))
5414 {
5415         line->set_position (pos);
5416         line->show ();
5417 }
5418
5419 RegionCutDrag::~RegionCutDrag ()
5420 {
5421         delete line;
5422 }
5423
5424 void
5425 RegionCutDrag::motion (GdkEvent*, bool)
5426 {
5427         framepos_t where = _drags->current_pointer_frame();
5428         _editor->snap_to (where);
5429
5430         line->set_position (where);
5431 }
5432
5433 void
5434 RegionCutDrag::finished (GdkEvent*, bool)
5435 {
5436         _editor->get_track_canvas()->canvas()->re_enter();
5437
5438         framepos_t pos = _drags->current_pointer_frame();
5439         
5440         line->hide ();
5441
5442         RegionSelection rs = _editor->get_regions_from_selection_and_mouse (pos);
5443
5444         if (rs.empty()) {
5445                 return;
5446         }
5447
5448         _editor->split_regions_at (pos, rs);
5449 }
5450
5451 void
5452 RegionCutDrag::aborted (bool)
5453 {
5454 }