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