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