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