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