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