range selection drag ensures that selected tracks include all tracks within the verti...
[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 movement_occured)
3322 {
3323         if (movement_occured) {
3324                 motion (event, false);
3325                 _line->end_drag (false, 0);
3326         } else {
3327                 /* add a new control point on the line */
3328
3329                 AutomationTimeAxisView* atv;
3330
3331                 _line->end_drag (false, 0);
3332
3333                 if ((atv = dynamic_cast<AutomationTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3334                         framepos_t where = _editor->window_event_frame (event, 0, 0);
3335                         atv->add_automation_event (event, where, event->button.y, false);
3336                 }
3337         }
3338
3339         _editor->session()->commit_reversible_command ();
3340 }
3341
3342 void
3343 LineDrag::aborted (bool)
3344 {
3345         _line->reset ();
3346 }
3347
3348 FeatureLineDrag::FeatureLineDrag (Editor* e, ArdourCanvas::Item* i)
3349         : Drag (e, i),
3350           _line (0),
3351           _cumulative_x_drag (0)
3352 {
3353         DEBUG_TRACE (DEBUG::Drags, "New FeatureLineDrag\n");
3354 }
3355
3356 void
3357 FeatureLineDrag::start_grab (GdkEvent* event, Gdk::Cursor* /*cursor*/)
3358 {
3359         Drag::start_grab (event);
3360
3361         _line = reinterpret_cast<Line*> (_item);
3362         assert (_line);
3363
3364         /* need to get x coordinate in terms of parent (AudioRegionView) origin. */
3365
3366         double cx = event->button.x;
3367         double cy = event->button.y;
3368
3369         _item->parent()->canvas_to_item (cx, cy);
3370
3371         /* store grab start in parent frame */
3372         _region_view_grab_x = cx;
3373
3374         _before = *(float*) _item->get_data ("position");
3375
3376         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3377
3378         _max_x = _editor->sample_to_pixel(_arv->get_duration());
3379 }
3380
3381 void
3382 FeatureLineDrag::motion (GdkEvent*, bool)
3383 {
3384         double dx = _drags->current_pointer_x() - last_pointer_x();
3385
3386         double cx = _region_view_grab_x + _cumulative_x_drag + dx;
3387
3388         _cumulative_x_drag += dx;
3389
3390         /* Clamp the min and max extent of the drag to keep it within the region view bounds */
3391
3392         if (cx > _max_x){
3393                 cx = _max_x;
3394         }
3395         else if(cx < 0){
3396                 cx = 0;
3397         }
3398
3399         boost::optional<ArdourCanvas::Rect> bbox = _line->bounding_box ();
3400         assert (bbox);
3401         _line->set (ArdourCanvas::Duple (cx, 2.0), ArdourCanvas::Duple (cx, bbox.get().height ()));
3402
3403         float *pos = new float;
3404         *pos = cx;
3405
3406         _line->set_data ("position", pos);
3407
3408         _before = cx;
3409 }
3410
3411 void
3412 FeatureLineDrag::finished (GdkEvent*, bool)
3413 {
3414         _arv = reinterpret_cast<AudioRegionView*> (_item->get_data ("regionview"));
3415         _arv->update_transient(_before, _before);
3416 }
3417
3418 void
3419 FeatureLineDrag::aborted (bool)
3420 {
3421         //_line->reset ();
3422 }
3423
3424 RubberbandSelectDrag::RubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
3425         : Drag (e, i)
3426         , _vertical_only (false)
3427 {
3428         DEBUG_TRACE (DEBUG::Drags, "New RubberbandSelectDrag\n");
3429 }
3430
3431 void
3432 RubberbandSelectDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3433 {
3434         Drag::start_grab (event);
3435         show_verbose_cursor_time (adjusted_current_frame (event));
3436 }
3437
3438 void
3439 RubberbandSelectDrag::motion (GdkEvent* event, bool)
3440 {
3441         framepos_t start;
3442         framepos_t end;
3443         double y1;
3444         double y2;
3445
3446         framepos_t const pf = adjusted_current_frame (event, Config->get_rubberbanding_snaps_to_grid ());
3447
3448         framepos_t grab = grab_frame ();
3449         if (Config->get_rubberbanding_snaps_to_grid ()) {
3450                 _editor->snap_to_with_modifier (grab, event);
3451         }
3452
3453         /* base start and end on initial click position */
3454
3455         if (pf < grab) {
3456                 start = pf;
3457                 end = grab;
3458         } else {
3459                 end = pf;
3460                 start = grab;
3461         }
3462
3463         if (_drags->current_pointer_y() < grab_y()) {
3464                 y1 = _drags->current_pointer_y();
3465                 y2 = grab_y();
3466         } else {
3467                 y2 = _drags->current_pointer_y();
3468                 y1 = grab_y();
3469         }
3470
3471
3472         if (start != end || y1 != y2) {
3473
3474                 double x1 = _editor->sample_to_pixel (start);
3475                 double x2 = _editor->sample_to_pixel (end);
3476                 const double min_dimension = 2.0;
3477
3478                 _editor->rubberband_rect->set_x0 (x1);
3479                 if (_vertical_only) {
3480                         /* fixed 10 pixel width */
3481                         _editor->rubberband_rect->set_x1 (x1 + 10);
3482                 } else {
3483                         if (x2 < x1) {
3484                                 x2 = min (x1 - min_dimension, x2);
3485                         } else {
3486                                 x2 = max (x1 + min_dimension, x2);
3487                         }
3488                         _editor->rubberband_rect->set_x1 (x2);
3489                 } 
3490
3491                 _editor->rubberband_rect->set_y0 (y1);
3492                 if (y2 < y1) {
3493                         y2 = min (y1 - min_dimension, y2);
3494                 } else {
3495                         y2 = max (y1 + min_dimension, y2);
3496                 }
3497
3498                 _editor->rubberband_rect->set_y1 (y2);
3499                 
3500                 _editor->rubberband_rect->show();
3501                 _editor->rubberband_rect->raise_to_top();
3502
3503                 show_verbose_cursor_time (pf);
3504
3505                 do_select_things (event, true);
3506         }
3507 }
3508
3509 void
3510 RubberbandSelectDrag::do_select_things (GdkEvent* event, bool drag_in_progress)
3511 {
3512         framepos_t x1;
3513         framepos_t x2;
3514         
3515         if (grab_frame() < last_pointer_frame()) {
3516                 x1 = grab_frame ();
3517                 x2 = last_pointer_frame ();
3518         } else {
3519                 x2 = grab_frame ();
3520                 x1 = last_pointer_frame ();
3521         }
3522
3523         double y1;
3524         double y2;
3525         
3526         if (_drags->current_pointer_y() < grab_y()) {
3527                 y1 = _drags->current_pointer_y();
3528                 y2 = grab_y();
3529         } else {
3530                 y2 = _drags->current_pointer_y();
3531                 y1 = grab_y();
3532         }
3533
3534         select_things (event->button.state, x1, x2, y1, y2, drag_in_progress);
3535 }
3536
3537 void
3538 RubberbandSelectDrag::finished (GdkEvent* event, bool movement_occurred)
3539 {
3540         if (movement_occurred) {
3541
3542                 motion (event, false);
3543                 do_select_things (event, false);
3544
3545         } else {
3546
3547                 /* just a click */
3548
3549                 bool do_deselect = true;
3550                 MidiTimeAxisView* mtv;
3551
3552                 if ((mtv = dynamic_cast<MidiTimeAxisView*>(_editor->clicked_axisview)) != 0) {
3553                         /* MIDI track */
3554                         if (_editor->selection->empty()) {
3555                                 /* nothing selected */
3556                                 add_midi_region (mtv);
3557                                 do_deselect = false;
3558                         }
3559                 } 
3560
3561                 /* do not deselect if Primary or Tertiary (toggle-select or
3562                  * extend-select are pressed.
3563                  */
3564
3565                 if (!Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier) && 
3566                     !Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier) && 
3567                     do_deselect) {
3568                         deselect_things ();
3569                 }
3570
3571         }
3572
3573         _editor->rubberband_rect->hide();
3574 }
3575
3576 void
3577 RubberbandSelectDrag::aborted (bool)
3578 {
3579         _editor->rubberband_rect->hide ();
3580 }
3581
3582 TimeFXDrag::TimeFXDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, std::list<RegionView*> const & v)
3583         : RegionDrag (e, i, p, v)
3584 {
3585         DEBUG_TRACE (DEBUG::Drags, "New TimeFXDrag\n");
3586 }
3587
3588 void
3589 TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
3590 {
3591         Drag::start_grab (event, cursor);
3592
3593         show_verbose_cursor_time (adjusted_current_frame (event));
3594 }
3595
3596 void
3597 TimeFXDrag::motion (GdkEvent* event, bool)
3598 {
3599         RegionView* rv = _primary;
3600         StreamView* cv = rv->get_time_axis_view().view ();
3601
3602         pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (grab_y());
3603         int layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
3604         int layers = tv.first->layer_display() == Overlaid ? 1 : cv->layers();
3605
3606         framepos_t const pf = adjusted_current_frame (event);
3607
3608         if (pf > rv->region()->position()) {
3609                 rv->get_time_axis_view().show_timestretch (rv->region()->position(), pf, layers, layer);
3610         }
3611
3612         show_verbose_cursor_time (pf);
3613 }
3614
3615 void
3616 TimeFXDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3617 {
3618         _primary->get_time_axis_view().hide_timestretch ();
3619
3620         if (!movement_occurred) {
3621                 return;
3622         }
3623
3624         if (last_pointer_frame() < _primary->region()->position()) {
3625                 /* backwards drag of the left edge - not usable */
3626                 return;
3627         }
3628
3629         framecnt_t newlen = last_pointer_frame() - _primary->region()->position();
3630
3631         float percentage = (double) newlen / (double) _primary->region()->length();
3632
3633 #ifndef USE_RUBBERBAND
3634         // Soundtouch uses percentage / 100 instead of normal (/ 1)
3635         if (_primary->region()->data_type() == DataType::AUDIO) {
3636                 percentage = (float) ((double) newlen - (double) _primary->region()->length()) / ((double) newlen) * 100.0f;
3637         }
3638 #endif
3639
3640         if (!_editor->get_selection().regions.empty()) {
3641                 /* primary will already be included in the selection, and edit
3642                    group shared editing will propagate selection across
3643                    equivalent regions, so just use the current region
3644                    selection.
3645                 */
3646
3647                 if (_editor->time_stretch (_editor->get_selection().regions, percentage) == -1) {
3648                         error << _("An error occurred while executing time stretch operation") << endmsg;
3649                 }
3650         }
3651 }
3652
3653 void
3654 TimeFXDrag::aborted (bool)
3655 {
3656         _primary->get_time_axis_view().hide_timestretch ();
3657 }
3658
3659 ScrubDrag::ScrubDrag (Editor* e, ArdourCanvas::Item* i)
3660         : Drag (e, i)
3661 {
3662         DEBUG_TRACE (DEBUG::Drags, "New ScrubDrag\n");
3663 }
3664
3665 void
3666 ScrubDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
3667 {
3668         Drag::start_grab (event);
3669 }
3670
3671 void
3672 ScrubDrag::motion (GdkEvent* /*event*/, bool)
3673 {
3674         _editor->scrub (adjusted_current_frame (0, false), _drags->current_pointer_x ());
3675 }
3676
3677 void
3678 ScrubDrag::finished (GdkEvent* /*event*/, bool movement_occurred)
3679 {
3680         if (movement_occurred && _editor->session()) {
3681                 /* make sure we stop */
3682                 _editor->session()->request_transport_speed (0.0);
3683         }
3684 }
3685
3686 void
3687 ScrubDrag::aborted (bool)
3688 {
3689         /* XXX: TODO */
3690 }
3691
3692 SelectionDrag::SelectionDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
3693         : Drag (e, i)
3694         , _operation (o)
3695         , _add (false)
3696         , _extend (false)
3697         , _original_pointer_time_axis (-1)
3698         , _last_pointer_time_axis (-1)
3699         , _time_selection_at_start (!_editor->get_selection().time.empty())
3700 {
3701         DEBUG_TRACE (DEBUG::Drags, "New SelectionDrag\n");
3702         
3703         if (_time_selection_at_start) {
3704                 start_at_start = _editor->get_selection().time.start();
3705                 end_at_start = _editor->get_selection().time.end_frame();
3706         }
3707 }
3708
3709 void
3710 SelectionDrag::start_grab (GdkEvent* event, Gdk::Cursor*)
3711 {
3712         if (_editor->session() == 0) {
3713                 return;
3714         }
3715
3716         Gdk::Cursor* cursor = 0;
3717
3718         switch (_operation) {
3719         case CreateSelection:
3720                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3721                         _add = true;
3722                 } else {
3723                         _add = false;
3724                 }
3725                 cursor = _editor->cursors()->selector;
3726                 Drag::start_grab (event, cursor);
3727                 break;
3728
3729         case SelectionStartTrim:
3730                 if (_editor->clicked_axisview) {
3731                         _editor->clicked_axisview->order_selection_trims (_item, true);
3732                 }
3733                 Drag::start_grab (event, _editor->cursors()->left_side_trim);
3734                 break;
3735
3736         case SelectionEndTrim:
3737                 if (_editor->clicked_axisview) {
3738                         _editor->clicked_axisview->order_selection_trims (_item, false);
3739                 }
3740                 Drag::start_grab (event, _editor->cursors()->right_side_trim);
3741                 break;
3742
3743         case SelectionMove:
3744                 Drag::start_grab (event, cursor);
3745                 break;
3746
3747         case SelectionExtend:
3748                 Drag::start_grab (event, cursor);
3749                 break;
3750         }
3751
3752         if (_operation == SelectionMove) {
3753                 show_verbose_cursor_time (_editor->selection->time[_editor->clicked_selection].start);
3754         } else {
3755                 show_verbose_cursor_time (adjusted_current_frame (event));
3756         }
3757
3758         _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
3759 }
3760
3761 void
3762 SelectionDrag::setup_pointer_frame_offset ()
3763 {
3764         switch (_operation) {
3765         case CreateSelection:
3766                 _pointer_frame_offset = 0;
3767                 break;
3768
3769         case SelectionStartTrim:
3770         case SelectionMove:
3771                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].start;
3772                 break;
3773
3774         case SelectionEndTrim:
3775                 _pointer_frame_offset = raw_grab_frame() - _editor->selection->time[_editor->clicked_selection].end;
3776                 break;
3777
3778         case SelectionExtend:
3779                 break;
3780         }
3781 }
3782
3783 void
3784 SelectionDrag::motion (GdkEvent* event, bool first_move)
3785 {
3786         framepos_t start = 0;
3787         framepos_t end = 0;
3788         framecnt_t length = 0;
3789         framecnt_t distance = 0;
3790
3791         framepos_t const pending_position = adjusted_current_frame (event);
3792
3793         if (pending_position == last_pointer_frame()) {
3794                 return;
3795         }
3796
3797         switch (_operation) {
3798         case CreateSelection:
3799         {
3800                 framepos_t grab = grab_frame ();
3801
3802                 if (first_move) {
3803                         grab = adjusted_current_frame (event, false);
3804                         if (grab < pending_position) {
3805                                 _editor->snap_to (grab, -1);
3806                         }  else {
3807                                 _editor->snap_to (grab, 1);
3808                         }
3809                 }
3810
3811                 if (pending_position < grab) {
3812                         start = pending_position;
3813                         end = grab;
3814                 } else {
3815                         end = pending_position;
3816                         start = grab;
3817                 }
3818
3819                 /* first drag: Either add to the selection
3820                    or create a new selection
3821                 */
3822
3823                 if (first_move) {
3824
3825                         if (_add) {
3826
3827                                 /* adding to the selection */
3828                                 _editor->set_selected_track_as_side_effect (Selection::Add);
3829                                 _editor->clicked_selection = _editor->selection->add (start, end);
3830                                 _add = false;
3831                                 
3832                         } else {
3833
3834                                 /* new selection */
3835
3836                                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3837                                         _editor->set_selected_track_as_side_effect (Selection::Set);
3838                                 }
3839
3840                                 _editor->clicked_selection = _editor->selection->set (start, end);
3841                         }
3842                 }
3843                 
3844                 /* select all tracks within the rectangle that we've marked out so far */
3845                 TrackViewList to_be_added_to_selection;
3846                 TrackViewList to_be_removed_from_selection;
3847                 TrackViewList& all_tracks (_editor->track_views);
3848
3849                 for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
3850                         
3851                         if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
3852                                 if (!(*i)->get_selected()) {
3853                                         to_be_added_to_selection.push_back (*i);
3854                                 }
3855                         } else {
3856                                 if ((*i)->get_selected()) {
3857                                         to_be_removed_from_selection.push_back (*i);
3858                                 }
3859                         }
3860                 }
3861
3862                 if (!to_be_added_to_selection.empty()) {
3863                         _editor->selection->add (to_be_added_to_selection);
3864                 }
3865
3866                 if (!to_be_removed_from_selection.empty()) {
3867                         _editor->selection->remove (to_be_removed_from_selection);
3868                 }
3869         }
3870         break;
3871
3872         case SelectionStartTrim:
3873
3874                 start = _editor->selection->time[_editor->clicked_selection].start;
3875                 end = _editor->selection->time[_editor->clicked_selection].end;
3876
3877                 if (pending_position > end) {
3878                         start = end;
3879                 } else {
3880                         start = pending_position;
3881                 }
3882                 break;
3883
3884         case SelectionEndTrim:
3885
3886                 start = _editor->selection->time[_editor->clicked_selection].start;
3887                 end = _editor->selection->time[_editor->clicked_selection].end;
3888
3889                 if (pending_position < start) {
3890                         end = start;
3891                 } else {
3892                         end = pending_position;
3893                 }
3894
3895                 break;
3896                 
3897         case SelectionMove:
3898
3899                 start = _editor->selection->time[_editor->clicked_selection].start;
3900                 end = _editor->selection->time[_editor->clicked_selection].end;
3901
3902                 length = end - start;
3903                 distance = pending_position - start;
3904                 start = pending_position;
3905                 _editor->snap_to (start);
3906
3907                 end = start + length;
3908
3909                 break;
3910
3911         case SelectionExtend:
3912                 break;
3913         }
3914
3915         if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
3916                 _editor->start_canvas_autoscroll (1, 0);
3917         }
3918
3919         if (start != end) {
3920                 switch (_operation) {
3921                 case SelectionMove:     
3922                         if (_time_selection_at_start) {
3923                                 _editor->selection->move_time (distance);
3924                         }
3925                         break;
3926                 default:
3927                         _editor->selection->replace (_editor->clicked_selection, start, end);
3928                 }
3929         }
3930
3931         if (_operation == SelectionMove) {
3932                 show_verbose_cursor_time(start);
3933         } else {
3934                 show_verbose_cursor_time(pending_position);
3935         }
3936 }
3937
3938 void
3939 SelectionDrag::finished (GdkEvent* event, bool movement_occurred)
3940 {
3941         Session* s = _editor->session();
3942
3943         if (movement_occurred) {
3944                 motion (event, false);
3945                 /* XXX this is not object-oriented programming at all. ick */
3946                 if (_editor->selection->time.consolidate()) {
3947                         _editor->selection->TimeChanged ();
3948                 }
3949
3950                 /* XXX what if its a music time selection? */
3951                 if (s) {
3952                         if ( s->get_play_range() && s->transport_rolling() ) {
3953                                 s->request_play_range (&_editor->selection->time, true);
3954                         } else {
3955                                 if (Config->get_always_play_range() && !s->transport_rolling()) {
3956                                         s->request_locate (_editor->get_selection().time.start());
3957                                 }
3958                         }
3959                 }
3960
3961         } else {
3962                 /* just a click, no pointer movement.
3963                  */
3964
3965                 if (_operation == SelectionExtend) {
3966                         if (_time_selection_at_start) {
3967                                 framepos_t pos = adjusted_current_frame (event, false);
3968                                 framepos_t start = min (pos, start_at_start);
3969                                 framepos_t end = max (pos, end_at_start);
3970                                 _editor->selection->set (start, end);
3971                         }
3972                 } else {
3973                         if (Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)) {
3974                                 if (_editor->clicked_selection) {
3975                                         _editor->selection->remove (_editor->clicked_selection);
3976                                 }
3977                         } else {
3978                                 if (!_editor->clicked_selection) {
3979                                         _editor->selection->clear_time();
3980                                 }
3981                         }
3982                 }
3983
3984                 if (_editor->clicked_axisview && !_editor->selection->selected (_editor->clicked_axisview)) {
3985                         _editor->selection->set (_editor->clicked_axisview);
3986                 }
3987                         
3988                 if (s && s->get_play_range () && s->transport_rolling()) {
3989                         s->request_stop (false, false);
3990                 }
3991
3992         }
3993
3994         _editor->stop_canvas_autoscroll ();
3995         _editor->clicked_selection = 0;
3996 }
3997
3998 void
3999 SelectionDrag::aborted (bool)
4000 {
4001         /* XXX: TODO */
4002 }
4003
4004 RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
4005         : Drag (e, i),
4006           _operation (o),
4007           _copy (false)
4008 {
4009         DEBUG_TRACE (DEBUG::Drags, "New RangeMarkerBarDrag\n");
4010
4011         _drag_rect = new ArdourCanvas::Rectangle (_editor->time_line_group, 
4012                                                   ArdourCanvas::Rect (0.0, 0.0, 0.0,
4013                                                                       physical_screen_height (_editor->get_window())));
4014         _drag_rect->hide ();
4015
4016         _drag_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4017         _drag_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_RangeDragRect());
4018 }
4019
4020 void
4021 RangeMarkerBarDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4022 {
4023         if (_editor->session() == 0) {
4024                 return;
4025         }
4026
4027         Gdk::Cursor* cursor = 0;
4028
4029         if (!_editor->temp_location) {
4030                 _editor->temp_location = new Location (*_editor->session());
4031         }
4032
4033         switch (_operation) {
4034         case CreateRangeMarker:
4035         case CreateTransportMarker:
4036         case CreateCDMarker:
4037
4038                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
4039                         _copy = true;
4040                 } else {
4041                         _copy = false;
4042                 }
4043                 cursor = _editor->cursors()->selector;
4044                 break;
4045         }
4046
4047         Drag::start_grab (event, cursor);
4048
4049         show_verbose_cursor_time (adjusted_current_frame (event));
4050 }
4051
4052 void
4053 RangeMarkerBarDrag::motion (GdkEvent* event, bool first_move)
4054 {
4055         framepos_t start = 0;
4056         framepos_t end = 0;
4057         ArdourCanvas::Rectangle *crect;
4058
4059         switch (_operation) {
4060         case CreateRangeMarker:
4061                 crect = _editor->range_bar_drag_rect;
4062                 break;
4063         case CreateTransportMarker:
4064                 crect = _editor->transport_bar_drag_rect;
4065                 break;
4066         case CreateCDMarker:
4067                 crect = _editor->cd_marker_bar_drag_rect;
4068                 break;
4069         default:
4070                 error << string_compose (_("programming_error: %1"), "Error: unknown range marker op passed to Editor::drag_range_markerbar_op ()") << endmsg;
4071                 return;
4072                 break;
4073         }
4074
4075         framepos_t const pf = adjusted_current_frame (event);
4076
4077         if (_operation == CreateRangeMarker || _operation == CreateTransportMarker || _operation == CreateCDMarker) {
4078                 framepos_t grab = grab_frame ();
4079                 _editor->snap_to (grab);
4080
4081                 if (pf < grab_frame()) {
4082                         start = pf;
4083                         end = grab;
4084                 } else {
4085                         end = pf;
4086                         start = grab;
4087                 }
4088
4089                 /* first drag: Either add to the selection
4090                    or create a new selection.
4091                 */
4092
4093                 if (first_move) {
4094
4095                         _editor->temp_location->set (start, end);
4096
4097                         crect->show ();
4098
4099                         update_item (_editor->temp_location);
4100                         _drag_rect->show();
4101                         //_drag_rect->raise_to_top();
4102
4103                 }
4104         }
4105
4106         if (event->button.x >= _editor->horizontal_position() + _editor->_visible_canvas_width) {
4107                 _editor->start_canvas_autoscroll (1, 0);
4108         }
4109
4110         if (start != end) {
4111                 _editor->temp_location->set (start, end);
4112
4113                 double x1 = _editor->sample_to_pixel (start);
4114                 double x2 = _editor->sample_to_pixel (end);
4115                 crect->set_x0 (x1);
4116                 crect->set_x1 (x2);
4117
4118                 update_item (_editor->temp_location);
4119         }
4120
4121         show_verbose_cursor_time (pf);
4122
4123 }
4124
4125 void
4126 RangeMarkerBarDrag::finished (GdkEvent* event, bool movement_occurred)
4127 {
4128         Location * newloc = 0;
4129         string rangename;
4130         int flags;
4131
4132         if (movement_occurred) {
4133                 motion (event, false);
4134                 _drag_rect->hide();
4135
4136                 switch (_operation) {
4137                 case CreateRangeMarker:
4138                 case CreateCDMarker:
4139                     {
4140                         _editor->begin_reversible_command (_("new range marker"));
4141                         XMLNode &before = _editor->session()->locations()->get_state();
4142                         _editor->session()->locations()->next_available_name(rangename,"unnamed");
4143                         if (_operation == CreateCDMarker) {
4144                                 flags = Location::IsRangeMarker | Location::IsCDMarker;
4145                                 _editor->cd_marker_bar_drag_rect->hide();
4146                         }
4147                         else {
4148                                 flags = Location::IsRangeMarker;
4149                                 _editor->range_bar_drag_rect->hide();
4150                         }
4151                         newloc = new Location (
4152                                 *_editor->session(), _editor->temp_location->start(), _editor->temp_location->end(), rangename, (Location::Flags) flags
4153                                 );
4154
4155                         _editor->session()->locations()->add (newloc, true);
4156                         XMLNode &after = _editor->session()->locations()->get_state();
4157                         _editor->session()->add_command(new MementoCommand<Locations>(*(_editor->session()->locations()), &before, &after));
4158                         _editor->commit_reversible_command ();
4159                         break;
4160                     }
4161
4162                 case CreateTransportMarker:
4163                         // popup menu to pick loop or punch
4164                         _editor->new_transport_marker_context_menu (&event->button, _item);
4165                         break;
4166                 }
4167
4168         } else {
4169
4170                 /* just a click, no pointer movement. remember that context menu stuff was handled elsewhere */
4171
4172                 if (_operation == CreateTransportMarker) {
4173
4174                         /* didn't drag, so just locate */
4175
4176                         _editor->session()->request_locate (grab_frame(), _editor->session()->transport_rolling());
4177
4178                 } else if (_operation == CreateCDMarker) {
4179
4180                         /* didn't drag, but mark is already created so do
4181                          * nothing */
4182
4183                 } else { /* operation == CreateRangeMarker */
4184                         
4185
4186                         framepos_t start;
4187                         framepos_t end;
4188
4189                         _editor->session()->locations()->marks_either_side (grab_frame(), start, end);
4190
4191                         if (end == max_framepos) {
4192                                 end = _editor->session()->current_end_frame ();
4193                         }
4194
4195                         if (start == max_framepos) {
4196                                 start = _editor->session()->current_start_frame ();
4197                         }
4198
4199                         switch (_editor->mouse_mode) {
4200                         case MouseObject:
4201                                 /* find the two markers on either side and then make the selection from it */
4202                                 _editor->select_all_within (start, end, 0.0f, FLT_MAX, _editor->track_views, Selection::Set, false);
4203                                 break;
4204
4205                         case MouseRange:
4206                                 /* find the two markers on either side of the click and make the range out of it */
4207                                 _editor->selection->set (start, end);
4208                                 break;
4209
4210                         default:
4211                                 break;
4212                         }
4213                 }
4214         }
4215
4216         _editor->stop_canvas_autoscroll ();
4217 }
4218
4219 void
4220 RangeMarkerBarDrag::aborted (bool)
4221 {
4222         /* XXX: TODO */
4223 }
4224
4225 void
4226 RangeMarkerBarDrag::update_item (Location* location)
4227 {
4228         double const x1 = _editor->sample_to_pixel (location->start());
4229         double const x2 = _editor->sample_to_pixel (location->end());
4230
4231         _drag_rect->set_x0 (x1);
4232         _drag_rect->set_x1 (x2);
4233 }
4234
4235 MouseZoomDrag::MouseZoomDrag (Editor* e, ArdourCanvas::Item* i)
4236         : Drag (e, i)
4237         , _zoom_out (false)
4238 {
4239         DEBUG_TRACE (DEBUG::Drags, "New MouseZoomDrag\n");
4240 }
4241
4242 void
4243 MouseZoomDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4244 {
4245         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
4246                 Drag::start_grab (event, _editor->cursors()->zoom_out);
4247                 _zoom_out = true;
4248         } else {
4249                 Drag::start_grab (event, _editor->cursors()->zoom_in);
4250                 _zoom_out = false;
4251         }
4252
4253         show_verbose_cursor_time (adjusted_current_frame (event));
4254 }
4255
4256 void
4257 MouseZoomDrag::motion (GdkEvent* event, bool first_move)
4258 {
4259         framepos_t start;
4260         framepos_t end;
4261
4262         framepos_t const pf = adjusted_current_frame (event);
4263
4264         framepos_t grab = grab_frame ();
4265         _editor->snap_to_with_modifier (grab, event);
4266
4267         /* base start and end on initial click position */
4268         if (pf < grab) {
4269                 start = pf;
4270                 end = grab;
4271         } else {
4272                 end = pf;
4273                 start = grab;
4274         }
4275
4276         if (start != end) {
4277
4278                 if (first_move) {
4279                         _editor->zoom_rect->show();
4280                         _editor->zoom_rect->raise_to_top();
4281                 }
4282
4283                 _editor->reposition_zoom_rect(start, end);
4284
4285                 show_verbose_cursor_time (pf);
4286         }
4287 }
4288
4289 void
4290 MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred)
4291 {
4292         if (movement_occurred) {
4293                 motion (event, false);
4294
4295                 if (grab_frame() < last_pointer_frame()) {
4296                         _editor->temporal_zoom_by_frame (grab_frame(), last_pointer_frame());
4297                 } else {
4298                         _editor->temporal_zoom_by_frame (last_pointer_frame(), grab_frame());
4299                 }
4300         } else {
4301                 if (Keyboard::the_keyboard().key_is_down (GDK_Shift_L)) {
4302                         _editor->tav_zoom_step (_zoom_out);
4303                 } else {
4304                         _editor->temporal_zoom_to_frame (_zoom_out, grab_frame());
4305                 }
4306         }
4307
4308         _editor->zoom_rect->hide();
4309 }
4310
4311 void
4312 MouseZoomDrag::aborted (bool)
4313 {
4314         _editor->zoom_rect->hide ();
4315 }
4316
4317 NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i)
4318         : Drag (e, i)
4319         , _cumulative_dx (0)
4320         , _cumulative_dy (0)
4321 {
4322         DEBUG_TRACE (DEBUG::Drags, "New NoteDrag\n");
4323
4324         _primary = reinterpret_cast<NoteBase*> (_item->get_data ("notebase"));
4325         assert (_primary);
4326         _region = &_primary->region_view ();
4327         _note_height = _region->midi_stream_view()->note_height ();
4328 }
4329
4330 void
4331 NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *)
4332 {
4333         Drag::start_grab (event);
4334
4335         if (!(_was_selected = _primary->selected())) {
4336
4337                 /* tertiary-click means extend selection - we'll do that on button release,
4338                    so don't add it here, because otherwise we make it hard to figure
4339                    out the "extend-to" range.
4340                 */
4341
4342                 bool extend = Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier);
4343
4344                 if (!extend) {
4345                         bool add = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
4346
4347                         if (add) {
4348                                 _region->note_selected (_primary, true);
4349                         } else {
4350                                 _region->unique_select (_primary);
4351                         }
4352                 }
4353         }
4354 }
4355
4356 /** @return Current total drag x change in frames */
4357 frameoffset_t
4358 NoteDrag::total_dx () const
4359 {
4360         /* dx in frames */
4361         frameoffset_t const dx = _editor->pixel_to_sample (_drags->current_pointer_x() - grab_x());
4362
4363         /* primary note time */
4364         frameoffset_t const n = _region->source_beats_to_absolute_frames (_primary->note()->time ());
4365
4366         /* new time of the primary note in session frames */
4367         frameoffset_t st = n + dx;
4368
4369         framepos_t const rp = _region->region()->position ();
4370
4371         /* prevent the note being dragged earlier than the region's position */
4372         st = max (st, rp);
4373
4374         /* snap and return corresponding delta */
4375         return _region->snap_frame_to_frame (st - rp) + rp - n;
4376 }
4377
4378 /** @return Current total drag y change in note number */
4379 int8_t
4380 NoteDrag::total_dy () const
4381 {
4382         MidiStreamView* msv = _region->midi_stream_view ();
4383         double const y = _region->midi_view()->y_position ();
4384         /* new current note */
4385         uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
4386         /* clamp */
4387         n = max (msv->lowest_note(), n);
4388         n = min (msv->highest_note(), n);
4389         /* and work out delta */
4390         return n - msv->y_to_note (grab_y() - y);
4391 }
4392
4393 void
4394 NoteDrag::motion (GdkEvent *, bool)
4395 {
4396         /* Total change in x and y since the start of the drag */
4397         frameoffset_t const dx = total_dx ();
4398         int8_t const dy = total_dy ();
4399
4400         /* Now work out what we have to do to the note canvas items to set this new drag delta */
4401         double const tdx = _editor->sample_to_pixel (dx) - _cumulative_dx;
4402         double const tdy = -dy * _note_height - _cumulative_dy;
4403
4404         if (tdx || tdy) {
4405                 _cumulative_dx += tdx;
4406                 _cumulative_dy += tdy;
4407
4408                 int8_t note_delta = total_dy();
4409
4410                 _region->move_selection (tdx, tdy, note_delta);
4411
4412                 /* the new note value may be the same as the old one, but we
4413                  * don't know what that means because the selection may have
4414                  * involved more than one note and we might be doing something
4415                  * odd with them. so show the note value anyway, always.
4416                  */
4417
4418                 char buf[12];
4419                 uint8_t new_note = min (max (_primary->note()->note() + note_delta, 0), 127);
4420                 
4421                 snprintf (buf, sizeof (buf), "%s (%d)", Evoral::midi_note_name (new_note).c_str(),
4422                           (int) floor ((double)new_note));
4423
4424                 show_verbose_cursor_text (buf);
4425         }
4426 }
4427
4428 void
4429 NoteDrag::finished (GdkEvent* ev, bool moved)
4430 {
4431         if (!moved) {
4432                 /* no motion - select note */
4433                 
4434                 if (_editor->current_mouse_mode() == Editing::MouseObject ||
4435                     _editor->current_mouse_mode() == Editing::MouseDraw) {
4436                         
4437                         if (_was_selected) {
4438                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4439                                 if (add) {
4440                                         _region->note_deselected (_primary);
4441                                 }
4442                         } else {
4443                                 bool extend = Keyboard::modifier_state_equals (ev->button.state, Keyboard::TertiaryModifier);
4444                                 bool add = Keyboard::modifier_state_equals (ev->button.state, Keyboard::PrimaryModifier);
4445
4446                                 if (!extend && !add && _region->selection_size() > 1) {
4447                                         _region->unique_select (_primary);
4448                                 } else if (extend) {
4449                                         _region->note_selected (_primary, true, true);
4450                                 } else {
4451                                         /* it was added during button press */
4452                                 }
4453                         }
4454                 }
4455         } else {
4456                 _region->note_dropped (_primary, total_dx(), total_dy());
4457         }
4458 }
4459
4460 void
4461 NoteDrag::aborted (bool)
4462 {
4463         /* XXX: TODO */
4464 }
4465
4466 /** Make an AutomationRangeDrag for lines in an AutomationTimeAxisView */
4467 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AutomationTimeAxisView* atv, list<AudioRange> const & r)
4468         : Drag (editor, atv->base_item ())
4469         , _ranges (r)
4470         , _nothing_to_drag (false)
4471 {
4472         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4473         y_origin = atv->y_position();
4474         setup (atv->lines ());
4475 }
4476
4477 /** Make an AutomationRangeDrag for region gain lines */
4478 AutomationRangeDrag::AutomationRangeDrag (Editor* editor, AudioRegionView* rv, list<AudioRange> const & r)
4479         : Drag (editor, rv->get_canvas_group ())
4480         , _ranges (r)
4481         , _nothing_to_drag (false)
4482 {
4483         DEBUG_TRACE (DEBUG::Drags, "New AutomationRangeDrag\n");
4484
4485         list<boost::shared_ptr<AutomationLine> > lines;
4486         lines.push_back (rv->get_gain_line ());
4487         y_origin = rv->get_time_axis_view().y_position();
4488         setup (lines);
4489 }
4490
4491 /** @param lines AutomationLines to drag.
4492  *  @param offset Offset from the session start to the points in the AutomationLines.
4493  */
4494 void
4495 AutomationRangeDrag::setup (list<boost::shared_ptr<AutomationLine> > const & lines)
4496 {
4497         /* find the lines that overlap the ranges being dragged */
4498         list<boost::shared_ptr<AutomationLine> >::const_iterator i = lines.begin ();
4499         while (i != lines.end ()) {
4500                 list<boost::shared_ptr<AutomationLine> >::const_iterator j = i;
4501                 ++j;
4502
4503                 pair<framepos_t, framepos_t> r = (*i)->get_point_x_range ();
4504
4505                 /* check this range against all the AudioRanges that we are using */
4506                 list<AudioRange>::const_iterator k = _ranges.begin ();
4507                 while (k != _ranges.end()) {
4508                         if (k->coverage (r.first, r.second) != Evoral::OverlapNone) {
4509                                 break;
4510                         }
4511                         ++k;
4512                 }
4513
4514                 /* add it to our list if it overlaps at all */
4515                 if (k != _ranges.end()) {
4516                         Line n;
4517                         n.line = *i;
4518                         n.state = 0;
4519                         n.range = r;
4520                         _lines.push_back (n);
4521                 }
4522
4523                 i = j;
4524         }
4525
4526         /* Now ::lines contains the AutomationLines that somehow overlap our drag */
4527 }
4528
4529 double
4530 AutomationRangeDrag::y_fraction (boost::shared_ptr<AutomationLine> line, double global_y) const
4531 {
4532         return 1.0 - ((global_y - y_origin) / line->height());
4533 }
4534
4535 void
4536 AutomationRangeDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4537 {
4538         Drag::start_grab (event, cursor);
4539
4540         /* Get line states before we start changing things */
4541         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4542                 i->state = &i->line->get_state ();
4543                 i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
4544         }
4545
4546         if (_ranges.empty()) {
4547
4548                 /* No selected time ranges: drag all points */
4549                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4550                         uint32_t const N = i->line->npoints ();
4551                         for (uint32_t j = 0; j < N; ++j) {
4552                                 i->points.push_back (i->line->nth (j));
4553                         }
4554                 }
4555
4556         } else {
4557
4558                 for (list<AudioRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i) {
4559
4560                         framecnt_t const half = (i->start + i->end) / 2;
4561
4562                         /* find the line that this audio range starts in */
4563                         list<Line>::iterator j = _lines.begin();
4564                         while (j != _lines.end() && (j->range.first > i->start || j->range.second < i->start)) {
4565                                 ++j;
4566                         }
4567
4568                         if (j != _lines.end()) {
4569                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4570
4571                                 /* j is the line that this audio range starts in; fade into it;
4572                                    64 samples length plucked out of thin air.
4573                                 */
4574
4575                                 framepos_t a = i->start + 64;
4576                                 if (a > half) {
4577                                         a = half;
4578                                 }
4579
4580                                 double const p = j->line->time_converter().from (i->start - j->line->time_converter().origin_b ());
4581                                 double const q = j->line->time_converter().from (a - j->line->time_converter().origin_b ());
4582
4583                                 the_list->add (p, the_list->eval (p));
4584                                 the_list->add (q, the_list->eval (q));
4585                         }
4586
4587                         /* same thing for the end */
4588
4589                         j = _lines.begin();
4590                         while (j != _lines.end() && (j->range.first > i->end || j->range.second < i->end)) {
4591                                 ++j;
4592                         }
4593
4594                         if (j != _lines.end()) {
4595                                 boost::shared_ptr<AutomationList> the_list = j->line->the_list ();
4596
4597                                 /* j is the line that this audio range starts in; fade out of it;
4598                                    64 samples length plucked out of thin air.
4599                                 */
4600
4601                                 framepos_t b = i->end - 64;
4602                                 if (b < half) {
4603                                         b = half;
4604                                 }
4605
4606                                 double const p = j->line->time_converter().from (b - j->line->time_converter().origin_b ());
4607                                 double const q = j->line->time_converter().from (i->end - j->line->time_converter().origin_b ());
4608
4609                                 the_list->add (p, the_list->eval (p));
4610                                 the_list->add (q, the_list->eval (q));
4611                         }
4612                 }
4613
4614                 _nothing_to_drag = true;
4615
4616                 /* Find all the points that should be dragged and put them in the relevant
4617                    points lists in the Line structs.
4618                 */
4619
4620                 for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4621
4622                         uint32_t const N = i->line->npoints ();
4623                         for (uint32_t j = 0; j < N; ++j) {
4624
4625                                 /* here's a control point on this line */
4626                                 ControlPoint* p = i->line->nth (j);
4627                                 double const w = i->line->time_converter().to ((*p->model())->when) + i->line->time_converter().origin_b ();
4628
4629                                 /* see if it's inside a range */
4630                                 list<AudioRange>::const_iterator k = _ranges.begin ();
4631                                 while (k != _ranges.end() && (k->start >= w || k->end <= w)) {
4632                                         ++k;
4633                                 }
4634
4635                                 if (k != _ranges.end()) {
4636                                         /* dragging this point */
4637                                         _nothing_to_drag = false;
4638                                         i->points.push_back (p);
4639                                 }
4640                         }
4641                 }
4642         }
4643
4644         if (_nothing_to_drag) {
4645                 return;
4646         }
4647
4648         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4649                 i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
4650         }
4651 }
4652
4653 void
4654 AutomationRangeDrag::motion (GdkEvent*, bool /*first_move*/)
4655 {
4656         if (_nothing_to_drag) {
4657                 return;
4658         }
4659
4660         for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
4661                 float const f = y_fraction (l->line, _drags->current_pointer_y());
4662                 /* we are ignoring x position for this drag, so we can just pass in anything */
4663                 uint32_t ignored;
4664                 l->line->drag_motion (0, f, true, false, ignored);
4665                 show_verbose_cursor_text (l->line->get_verbose_cursor_relative_string (l->original_fraction, f));
4666         }
4667 }
4668
4669 void
4670 AutomationRangeDrag::finished (GdkEvent* event, bool)
4671 {
4672         if (_nothing_to_drag) {
4673                 return;
4674         }
4675
4676         motion (event, false);
4677         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4678                 i->line->end_drag (false, 0);
4679         }
4680
4681         _editor->session()->commit_reversible_command ();
4682 }
4683
4684 void
4685 AutomationRangeDrag::aborted (bool)
4686 {
4687         for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
4688                 i->line->reset ();
4689         }
4690 }
4691
4692 DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
4693         : view (v)
4694 {
4695         time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
4696         layer = v->region()->layer ();
4697         initial_y = v->get_canvas_group()->position().y;
4698         initial_playlist = v->region()->playlist ();
4699         initial_position = v->region()->position ();
4700         initial_end = v->region()->position () + v->region()->length ();
4701 }
4702
4703 PatchChangeDrag::PatchChangeDrag (Editor* e, PatchChange* i, MidiRegionView* r)
4704         : Drag (e, i->canvas_item ())
4705         , _region_view (r)
4706         , _patch_change (i)
4707         , _cumulative_dx (0)
4708 {
4709         DEBUG_TRACE (DEBUG::Drags, string_compose ("New PatchChangeDrag, patch @ %1, grab @ %2\n",
4710                                                    _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time()),
4711                                                    grab_frame()));
4712 }
4713
4714 void
4715 PatchChangeDrag::motion (GdkEvent* ev, bool)
4716 {
4717         framepos_t f = adjusted_current_frame (ev);
4718         boost::shared_ptr<Region> r = _region_view->region ();
4719         f = max (f, r->position ());
4720         f = min (f, r->last_frame ());
4721
4722         framecnt_t const dxf = f - grab_frame(); // permitted dx in frames
4723         double const dxu = _editor->sample_to_pixel (dxf); // permitted fx in units
4724         _patch_change->move (ArdourCanvas::Duple (dxu - _cumulative_dx, 0));
4725         _cumulative_dx = dxu;
4726 }
4727
4728 void
4729 PatchChangeDrag::finished (GdkEvent* ev, bool movement_occurred)
4730 {
4731         if (!movement_occurred) {
4732                 return;
4733         }
4734
4735         boost::shared_ptr<Region> r (_region_view->region ());
4736         framepos_t f = adjusted_current_frame (ev);
4737         f = max (f, r->position ());
4738         f = min (f, r->last_frame ());
4739
4740         _region_view->move_patch_change (
4741                 *_patch_change,
4742                 _region_view->region_frames_to_region_beats (f - (r->position() - r->start()))
4743                 );
4744 }
4745
4746 void
4747 PatchChangeDrag::aborted (bool)
4748 {
4749         _patch_change->move (ArdourCanvas::Duple (-_cumulative_dx, 0));
4750 }
4751
4752 void
4753 PatchChangeDrag::setup_pointer_frame_offset ()
4754 {
4755         boost::shared_ptr<Region> region = _region_view->region ();
4756         _pointer_frame_offset = raw_grab_frame() - _region_view->source_beats_to_absolute_frames (_patch_change->patch()->time());
4757 }
4758
4759 MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* rv)
4760         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4761         , _region_view (rv)
4762 {
4763
4764 }
4765
4766 void
4767 MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/)
4768 {
4769         framepos_t const p = _region_view->region()->position ();
4770         double const y = _region_view->midi_view()->y_position ();
4771
4772         x1 = max ((framepos_t) 0, x1 - p);
4773         x2 = max ((framepos_t) 0, x2 - p);
4774         y1 = max (0.0, y1 - y);
4775         y2 = max (0.0, y2 - y);
4776         
4777         _region_view->update_drag_selection (
4778                 _editor->sample_to_pixel (x1),
4779                 _editor->sample_to_pixel (x2),
4780                 y1,
4781                 y2,
4782                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4783                 );
4784 }
4785
4786 void
4787 MidiRubberbandSelectDrag::deselect_things ()
4788 {
4789         /* XXX */
4790 }
4791
4792 MidiVerticalSelectDrag::MidiVerticalSelectDrag (Editor* e, MidiRegionView* rv)
4793         : RubberbandSelectDrag (e, rv->get_canvas_group ())
4794         , _region_view (rv)
4795 {
4796         _vertical_only = true;
4797 }
4798
4799 void
4800 MidiVerticalSelectDrag::select_things (int button_state, framepos_t /*x1*/, framepos_t /*x2*/, double y1, double y2, bool /*drag_in_progress*/)
4801 {
4802         double const y = _region_view->midi_view()->y_position ();
4803
4804         y1 = max (0.0, y1 - y);
4805         y2 = max (0.0, y2 - y);
4806         
4807         _region_view->update_vertical_drag_selection (
4808                 y1,
4809                 y2,
4810                 Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)
4811                 );
4812 }
4813
4814 void
4815 MidiVerticalSelectDrag::deselect_things ()
4816 {
4817         /* XXX */
4818 }
4819
4820 EditorRubberbandSelectDrag::EditorRubberbandSelectDrag (Editor* e, ArdourCanvas::Item* i)
4821         : RubberbandSelectDrag (e, i)
4822 {
4823
4824 }
4825
4826 void
4827 EditorRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool drag_in_progress)
4828 {
4829         if (drag_in_progress) {
4830                 /* We just want to select things at the end of the drag, not during it */
4831                 return;
4832         }
4833         
4834         Selection::Operation op = ArdourKeyboard::selection_type (button_state);
4835         
4836         _editor->begin_reversible_command (_("rubberband selection"));
4837         _editor->select_all_within (x1, x2 - 1, y1, y2, _editor->track_views, op, false);
4838         _editor->commit_reversible_command ();
4839 }
4840
4841 void
4842 EditorRubberbandSelectDrag::deselect_things ()
4843 {
4844         if (!getenv("ARDOUR_SAE")) {
4845                 _editor->selection->clear_tracks();
4846         }
4847         _editor->selection->clear_regions();
4848         _editor->selection->clear_points ();
4849         _editor->selection->clear_lines ();
4850 }
4851
4852 NoteCreateDrag::NoteCreateDrag (Editor* e, ArdourCanvas::Item* i, MidiRegionView* rv)
4853         : Drag (e, i)
4854         , _region_view (rv)
4855         , _drag_rect (0)
4856 {
4857         
4858 }
4859
4860 NoteCreateDrag::~NoteCreateDrag ()
4861 {
4862         delete _drag_rect;
4863 }
4864
4865 framecnt_t
4866 NoteCreateDrag::grid_frames (framepos_t t) const
4867 {
4868         bool success;
4869         Evoral::MusicalTime grid_beats = _editor->get_grid_type_as_beats (success, t);
4870         if (!success) {
4871                 grid_beats = 1;
4872         }
4873
4874         return _region_view->region_beats_to_region_frames (grid_beats);
4875 }
4876
4877 void
4878 NoteCreateDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
4879 {
4880         Drag::start_grab (event, cursor);
4881                                                  
4882         _drag_rect = new ArdourCanvas::Rectangle (_region_view->get_canvas_group ());
4883
4884         framepos_t pf = _drags->current_pointer_frame ();
4885         framecnt_t const g = grid_frames (pf);
4886
4887         /* Hack so that we always snap to the note that we are over, instead of snapping
4888            to the next one if we're more than halfway through the one we're over.
4889         */
4890         if (_editor->snap_mode() == SnapNormal && pf > g / 2) {
4891                 pf -= g / 2;
4892         }
4893
4894         _note[0] = adjusted_frame (pf, event) - _region_view->region()->position ();
4895
4896         MidiStreamView* sv = _region_view->midi_stream_view ();
4897         double const x = _editor->sample_to_pixel (_note[0]);
4898         double const y = sv->note_to_y (sv->y_to_note (y_to_region (event->button.y)));
4899
4900         _drag_rect->set (ArdourCanvas::Rect (x, y, x, y + floor (_region_view->midi_stream_view()->note_height ())));
4901         _drag_rect->set_outline_what (0xff);
4902         _drag_rect->set_outline_color (0xffffff99);
4903         _drag_rect->set_fill_color (0xffffff66);
4904 }
4905
4906 void
4907 NoteCreateDrag::motion (GdkEvent* event, bool)
4908 {
4909         _note[1] = max ((framepos_t)0, adjusted_current_frame (event) - _region_view->region()->position ());
4910         double const x = _editor->sample_to_pixel (_note[1]);
4911         if (_note[1] > _note[0]) {
4912                 _drag_rect->set_x1 (x);
4913         } else {
4914                 _drag_rect->set_x0 (x);
4915         }
4916 }
4917
4918 void
4919 NoteCreateDrag::finished (GdkEvent*, bool had_movement)
4920 {
4921         if (!had_movement) {
4922                 return;
4923         }
4924         
4925         framepos_t const start = min (_note[0], _note[1]);
4926         framecnt_t length = (framecnt_t) fabs ((double)(_note[0] - _note[1]));
4927
4928         framecnt_t const g = grid_frames (start);
4929         double const one_tick = 1 / Timecode::BBT_Time::ticks_per_beat;
4930         
4931         if (_editor->snap_mode() == SnapNormal && length < g) {
4932                 length = g - one_tick;
4933         }
4934
4935         double const length_beats = max (one_tick, _region_view->region_frames_to_region_beats (length));
4936
4937         _region_view->create_note_at (start, _drag_rect->y0(), length_beats, false);
4938 }
4939
4940 double
4941 NoteCreateDrag::y_to_region (double y) const
4942 {
4943         double x = 0;
4944         _region_view->get_canvas_group()->canvas_to_item (x, y);
4945         return y;
4946 }
4947
4948 void
4949 NoteCreateDrag::aborted (bool)
4950 {
4951         
4952 }
4953
4954 CrossfadeEdgeDrag::CrossfadeEdgeDrag (Editor* e, AudioRegionView* rv, ArdourCanvas::Item* i, bool start_yn)
4955         : Drag (e, i)
4956         , arv (rv)
4957         , start (start_yn)
4958 {
4959         std::cout << ("CrossfadeEdgeDrag is DEPRECATED.  See TrimDrag::preserve_fade_anchor") << endl;
4960 }
4961
4962 void
4963 CrossfadeEdgeDrag::start_grab (GdkEvent* event, Gdk::Cursor *cursor)
4964 {
4965         Drag::start_grab (event, cursor);
4966 }
4967
4968 void
4969 CrossfadeEdgeDrag::motion (GdkEvent*, bool)
4970 {
4971         double distance;
4972         double new_length;
4973         framecnt_t len;
4974
4975         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
4976
4977         if (start) {
4978                 distance = _drags->current_pointer_x() - grab_x();
4979                 len = ar->fade_in()->back()->when;
4980         } else {
4981                 distance = grab_x() - _drags->current_pointer_x();
4982                 len = ar->fade_out()->back()->when;
4983         }
4984
4985         /* how long should it be ? */
4986
4987         new_length = len + _editor->pixel_to_sample (distance);
4988
4989         /* now check with the region that this is legal */
4990
4991         new_length = ar->verify_xfade_bounds (new_length, start);
4992
4993         if (start) {
4994                 arv->reset_fade_in_shape_width (ar, new_length);
4995         } else {
4996                 arv->reset_fade_out_shape_width (ar, new_length);
4997         }
4998 }
4999
5000 void
5001 CrossfadeEdgeDrag::finished (GdkEvent*, bool)
5002 {
5003         double distance;
5004         double new_length;
5005         framecnt_t len;
5006
5007         boost::shared_ptr<AudioRegion> ar (arv->audio_region());
5008
5009         if (start) {
5010                 distance = _drags->current_pointer_x() - grab_x();
5011                 len = ar->fade_in()->back()->when;
5012         } else {
5013                 distance = grab_x() - _drags->current_pointer_x();
5014                 len = ar->fade_out()->back()->when;
5015         }
5016
5017         new_length = ar->verify_xfade_bounds (len + _editor->pixel_to_sample (distance), start);
5018         
5019         _editor->begin_reversible_command ("xfade trim");
5020         ar->playlist()->clear_owned_changes (); 
5021
5022         if (start) {
5023                 ar->set_fade_in_length (new_length);
5024         } else {
5025                 ar->set_fade_out_length (new_length);
5026         }
5027
5028         /* Adjusting the xfade may affect other regions in the playlist, so we need
5029            to get undo Commands from the whole playlist rather than just the
5030            region.
5031         */
5032
5033         vector<Command*> cmds;
5034         ar->playlist()->rdiff (cmds);
5035         _editor->session()->add_commands (cmds);
5036         _editor->commit_reversible_command ();
5037
5038 }
5039
5040 void
5041 CrossfadeEdgeDrag::aborted (bool)
5042 {
5043         if (start) {
5044                 arv->redraw_start_xfade ();
5045         } else {
5046                 arv->redraw_end_xfade ();
5047         }
5048 }
5049