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