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