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