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