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